masonite-framework-orm 3.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. masonite_framework_orm-3.0.1.dist-info/METADATA +87 -0
  2. masonite_framework_orm-3.0.1.dist-info/RECORD +116 -0
  3. masonite_framework_orm-3.0.1.dist-info/WHEEL +5 -0
  4. masonite_framework_orm-3.0.1.dist-info/entry_points.txt +3 -0
  5. masonite_framework_orm-3.0.1.dist-info/licenses/LICENSE +21 -0
  6. masonite_framework_orm-3.0.1.dist-info/top_level.txt +1 -0
  7. masoniteorm/__init__.py +1 -0
  8. masoniteorm/collection/Collection.py +605 -0
  9. masoniteorm/collection/__init__.py +1 -0
  10. masoniteorm/commands/CanOverrideConfig.py +16 -0
  11. masoniteorm/commands/CanOverrideOptionsDefault.py +22 -0
  12. masoniteorm/commands/Command.py +6 -0
  13. masoniteorm/commands/Entry.py +43 -0
  14. masoniteorm/commands/MakeMigrationCommand.py +57 -0
  15. masoniteorm/commands/MakeModelCommand.py +78 -0
  16. masoniteorm/commands/MakeModelDocstringCommand.py +37 -0
  17. masoniteorm/commands/MakeObserverCommand.py +54 -0
  18. masoniteorm/commands/MakeSeedCommand.py +54 -0
  19. masoniteorm/commands/MigrateCommand.py +46 -0
  20. masoniteorm/commands/MigrateFreshCommand.py +41 -0
  21. masoniteorm/commands/MigrateRefreshCommand.py +41 -0
  22. masoniteorm/commands/MigrateResetCommand.py +25 -0
  23. masoniteorm/commands/MigrateRollbackCommand.py +26 -0
  24. masoniteorm/commands/MigrateStatusCommand.py +51 -0
  25. masoniteorm/commands/SeedRunCommand.py +35 -0
  26. masoniteorm/commands/ShellCommand.py +205 -0
  27. masoniteorm/commands/__init__.py +18 -0
  28. masoniteorm/commands/stubs/create_migration.stub +20 -0
  29. masoniteorm/commands/stubs/create_seed.stub +9 -0
  30. masoniteorm/commands/stubs/model.stub +9 -0
  31. masoniteorm/commands/stubs/observer.stub +101 -0
  32. masoniteorm/commands/stubs/table_migration.stub +19 -0
  33. masoniteorm/config.py +123 -0
  34. masoniteorm/connections/BaseConnection.py +101 -0
  35. masoniteorm/connections/ConnectionFactory.py +59 -0
  36. masoniteorm/connections/ConnectionResolver.py +132 -0
  37. masoniteorm/connections/MSSQLConnection.py +176 -0
  38. masoniteorm/connections/MySQLConnection.py +232 -0
  39. masoniteorm/connections/PostgresConnection.py +225 -0
  40. masoniteorm/connections/SQLiteConnection.py +179 -0
  41. masoniteorm/connections/__init__.py +6 -0
  42. masoniteorm/exceptions.py +38 -0
  43. masoniteorm/expressions/__init__.py +1 -0
  44. masoniteorm/expressions/expressions.py +288 -0
  45. masoniteorm/factories/Factory.py +112 -0
  46. masoniteorm/factories/__init__.py +1 -0
  47. masoniteorm/helpers/__init__.py +0 -0
  48. masoniteorm/helpers/misc.py +22 -0
  49. masoniteorm/migrations/Migration.py +330 -0
  50. masoniteorm/migrations/__init__.py +1 -0
  51. masoniteorm/models/MigrationModel.py +9 -0
  52. masoniteorm/models/Model.py +1209 -0
  53. masoniteorm/models/Model.pyi +1366 -0
  54. masoniteorm/models/Pivot.py +5 -0
  55. masoniteorm/models/__init__.py +1 -0
  56. masoniteorm/observers/ObservesEvents.py +27 -0
  57. masoniteorm/observers/__init__.py +1 -0
  58. masoniteorm/pagination/BasePaginator.py +10 -0
  59. masoniteorm/pagination/LengthAwarePaginator.py +34 -0
  60. masoniteorm/pagination/SimplePaginator.py +28 -0
  61. masoniteorm/pagination/__init__.py +2 -0
  62. masoniteorm/providers/ORMProvider.py +39 -0
  63. masoniteorm/providers/__init__.py +1 -0
  64. masoniteorm/query/EagerRelation.py +42 -0
  65. masoniteorm/query/QueryBuilder.py +2486 -0
  66. masoniteorm/query/__init__.py +1 -0
  67. masoniteorm/query/grammars/BaseGrammar.py +1027 -0
  68. masoniteorm/query/grammars/MSSQLGrammar.py +194 -0
  69. masoniteorm/query/grammars/MySQLGrammar.py +238 -0
  70. masoniteorm/query/grammars/PostgresGrammar.py +213 -0
  71. masoniteorm/query/grammars/SQLiteGrammar.py +228 -0
  72. masoniteorm/query/grammars/__init__.py +4 -0
  73. masoniteorm/query/processors/MSSQLPostProcessor.py +58 -0
  74. masoniteorm/query/processors/MySQLPostProcessor.py +48 -0
  75. masoniteorm/query/processors/PostgresPostProcessor.py +49 -0
  76. masoniteorm/query/processors/SQLitePostProcessor.py +49 -0
  77. masoniteorm/query/processors/__init__.py +4 -0
  78. masoniteorm/relationships/BaseRelationship.py +161 -0
  79. masoniteorm/relationships/BelongsTo.py +124 -0
  80. masoniteorm/relationships/BelongsToMany.py +604 -0
  81. masoniteorm/relationships/HasMany.py +66 -0
  82. masoniteorm/relationships/HasManyThrough.py +269 -0
  83. masoniteorm/relationships/HasOne.py +111 -0
  84. masoniteorm/relationships/HasOneThrough.py +275 -0
  85. masoniteorm/relationships/MorphMany.py +152 -0
  86. masoniteorm/relationships/MorphOne.py +156 -0
  87. masoniteorm/relationships/MorphTo.py +111 -0
  88. masoniteorm/relationships/MorphToMany.py +108 -0
  89. masoniteorm/relationships/__init__.py +10 -0
  90. masoniteorm/schema/Blueprint.py +1161 -0
  91. masoniteorm/schema/Column.py +144 -0
  92. masoniteorm/schema/ColumnDiff.py +0 -0
  93. masoniteorm/schema/Constraint.py +5 -0
  94. masoniteorm/schema/ForeignKeyConstraint.py +28 -0
  95. masoniteorm/schema/Index.py +5 -0
  96. masoniteorm/schema/Schema.py +359 -0
  97. masoniteorm/schema/Table.py +94 -0
  98. masoniteorm/schema/TableDiff.py +86 -0
  99. masoniteorm/schema/__init__.py +3 -0
  100. masoniteorm/schema/platforms/MSSQLPlatform.py +367 -0
  101. masoniteorm/schema/platforms/MySQLPlatform.py +513 -0
  102. masoniteorm/schema/platforms/Platform.py +97 -0
  103. masoniteorm/schema/platforms/PostgresPlatform.py +551 -0
  104. masoniteorm/schema/platforms/SQLitePlatform.py +481 -0
  105. masoniteorm/schema/platforms/__init__.py +4 -0
  106. masoniteorm/scopes/BaseScope.py +6 -0
  107. masoniteorm/scopes/SoftDeleteScope.py +56 -0
  108. masoniteorm/scopes/SoftDeletesMixin.py +13 -0
  109. masoniteorm/scopes/TimeStampsMixin.py +12 -0
  110. masoniteorm/scopes/TimeStampsScope.py +47 -0
  111. masoniteorm/scopes/UUIDPrimaryKeyMixin.py +8 -0
  112. masoniteorm/scopes/UUIDPrimaryKeyScope.py +51 -0
  113. masoniteorm/scopes/__init__.py +8 -0
  114. masoniteorm/scopes/scope.py +15 -0
  115. masoniteorm/seeds/Seeder.py +42 -0
  116. masoniteorm/seeds/__init__.py +1 -0
@@ -0,0 +1,2486 @@
1
+ import inspect
2
+ from copy import deepcopy
3
+ from datetime import datetime
4
+ from typing import Any, Callable, Dict, List, Optional
5
+
6
+ from ..collection.Collection import Collection
7
+ from ..config import load_config
8
+ from ..connections import ConnectionResolver
9
+ from ..exceptions import (
10
+ HTTP404,
11
+ ConnectionNotRegistered,
12
+ InvalidArgument,
13
+ ModelNotFound,
14
+ MultipleRecordsFound,
15
+ )
16
+ from ..expressions.expressions import (
17
+ AggregateExpression,
18
+ BetweenExpression,
19
+ FromTable,
20
+ GroupByExpression,
21
+ HavingExpression,
22
+ JoinClause,
23
+ OrderByExpression,
24
+ QueryExpression,
25
+ SelectExpression,
26
+ SubGroupExpression,
27
+ SubSelectExpression,
28
+ UpdateQueryExpression,
29
+ )
30
+ from ..models import Model
31
+ from ..observers import ObservesEvents
32
+ from ..pagination import LengthAwarePaginator, SimplePaginator
33
+ from ..schema import Schema
34
+ from ..scopes import BaseScope
35
+ from .EagerRelation import EagerRelations
36
+
37
+
38
+ class QueryBuilder(ObservesEvents):
39
+ """A builder class to manage the building and creation of query expressions."""
40
+
41
+ def __init__(
42
+ self,
43
+ grammar=None,
44
+ connection="default",
45
+ connection_class=None,
46
+ table=None,
47
+ connection_details=None,
48
+ connection_driver="default",
49
+ model=None,
50
+ scopes=None,
51
+ schema=None,
52
+ dry=False,
53
+ config_path=None,
54
+ ):
55
+ """QueryBuilder initializer
56
+
57
+ Arguments:
58
+ grammar {masoniteorm.grammar.Grammar} -- A grammar class.
59
+
60
+ Keyword Arguments:
61
+ connection {masoniteorm.connection.Connection} -- A connection class (default: {None})
62
+ table {str} -- the name of the table (default: {""})
63
+ """
64
+ self.config_path = config_path
65
+ self.grammar = grammar
66
+ self.table(table)
67
+ self.dry = dry
68
+ self._creates_related = {}
69
+ self.connection = connection
70
+ self.connection_class = connection_class
71
+ self._connection = None
72
+ self._connection_details = connection_details or {}
73
+ self._connection_driver = connection_driver
74
+ self._scopes = scopes or {}
75
+ self.lock = False
76
+ self._schema = schema
77
+ self._eager_relation = EagerRelations()
78
+ if model:
79
+ self._global_scopes = model._global_scopes
80
+ if model.__with__:
81
+ self.with_(model.__with__)
82
+ else:
83
+ self._global_scopes = {}
84
+
85
+ self.builder = self
86
+
87
+ self._columns = ()
88
+ self._creates = {}
89
+
90
+ self._sql = ""
91
+ self._bindings = ()
92
+
93
+ self._updates = ()
94
+
95
+ self._wheres = ()
96
+ self._order_by = ()
97
+ self._group_by = ()
98
+ self._joins = ()
99
+ self._having = ()
100
+ self._macros = {}
101
+
102
+ self._aggregates = ()
103
+
104
+ self._limit = False
105
+ self._offset = False
106
+ self._distinct = False
107
+ self._model = model
108
+ self.set_action("select")
109
+
110
+ if not self._connection_details:
111
+ resolver = load_config(config_path=self.config_path).DB
112
+ self._connection_details = resolver.get_connection_details()
113
+
114
+ self.on(connection)
115
+
116
+ if grammar:
117
+ self.grammar = grammar
118
+
119
+ if connection_class:
120
+ self.connection_class = connection_class
121
+
122
+ def _set_creates_related(self, fields: dict):
123
+ self._creates_related = fields
124
+ return self
125
+
126
+ def set_schema(self, schema):
127
+ self._schema = schema
128
+ return self
129
+
130
+ def shared_lock(self):
131
+ return self.make_lock("share")
132
+
133
+ def lock_for_update(self):
134
+ return self.make_lock("update")
135
+
136
+ def make_lock(self, lock):
137
+ self.lock = lock
138
+ return self
139
+
140
+ def reset(self):
141
+ """Resets the query builder instance so you can make multiple calls with the same builder instance"""
142
+
143
+ self.set_action("select")
144
+
145
+ self._updates = ()
146
+
147
+ self._wheres = ()
148
+ self._order_by = ()
149
+ self._group_by = ()
150
+ self._joins = ()
151
+ self._having = ()
152
+
153
+ return self
154
+
155
+ def get_connection_information(self):
156
+ return {
157
+ "host": self._connection_details.get(self.connection, {}).get(
158
+ "host"
159
+ ),
160
+ "database": self._connection_details.get(self.connection, {}).get(
161
+ "database"
162
+ ),
163
+ "user": self._connection_details.get(self.connection, {}).get(
164
+ "user"
165
+ ),
166
+ "port": self._connection_details.get(self.connection, {}).get(
167
+ "port"
168
+ ),
169
+ "password": self._connection_details.get(self.connection, {}).get(
170
+ "password"
171
+ ),
172
+ "prefix": self._connection_details.get(self.connection, {}).get(
173
+ "prefix"
174
+ ),
175
+ "options": self._connection_details.get(self.connection, {}).get(
176
+ "options", {}
177
+ ),
178
+ "full_details": self._connection_details.get(self.connection, {}),
179
+ }
180
+
181
+ def table(self, table, raw=False):
182
+ """Sets a table on the query builder
183
+
184
+ Arguments:
185
+ table {string} -- The name of the table
186
+
187
+ Returns:
188
+ self
189
+ """
190
+ if table:
191
+ self._table = FromTable(table, raw=raw)
192
+ else:
193
+ self._table = table
194
+ return self
195
+
196
+ def from_(self, table):
197
+ """Alias for the table method
198
+
199
+ Arguments:
200
+ table {string} -- The name of the table
201
+
202
+ Returns:
203
+ self
204
+ """
205
+ return self.table(table)
206
+
207
+ def from_raw(self, table):
208
+ """Alias for the table method
209
+
210
+ Arguments:
211
+ table {string} -- The name of the table
212
+
213
+ Returns:
214
+ self
215
+ """
216
+ return self.table(table, raw=True)
217
+
218
+ def table_raw(self, query):
219
+ """Sets a query on the query builder
220
+
221
+ Arguments:
222
+ query {string} -- The query to use for the table
223
+
224
+ Returns:
225
+ self
226
+ """
227
+ return self.from_raw(query)
228
+
229
+ def get_table_name(self):
230
+ """Sets a table on the query builder
231
+
232
+ Arguments:
233
+ table {string} -- The name of the table
234
+
235
+ Returns:
236
+ self
237
+ """
238
+ return self._table.name
239
+
240
+ def get_connection(self):
241
+ """Sets a table on the query builder
242
+
243
+ Arguments:
244
+ table {string} -- The name of the table
245
+
246
+ Returns:
247
+ self
248
+ """
249
+ return self.connection_class
250
+
251
+ def begin(self):
252
+ """Sets a table on the query builder
253
+
254
+ Arguments:
255
+ table {string} -- The name of the table
256
+
257
+ Returns:
258
+ self
259
+ """
260
+ return self.new_connection().begin()
261
+
262
+ def begin_transaction(self, *args, **kwargs):
263
+ return self.begin(*args, **kwargs)
264
+
265
+ def get_schema_builder(self):
266
+ return Schema(connection=self.connection_class, grammar=self.grammar)
267
+
268
+ def commit(self):
269
+ """Sets a table on the query builder
270
+
271
+ Arguments:
272
+ table {string} -- The name of the table
273
+
274
+ Returns:
275
+ self
276
+ """
277
+ return self._connection.commit()
278
+
279
+ def rollback(self):
280
+ """Sets a table on the query builder
281
+
282
+ Arguments:
283
+ table {string} -- The name of the table
284
+
285
+ Returns:
286
+ self
287
+ """
288
+ self._connection.rollback()
289
+ return self
290
+
291
+ def get_relation(self, key):
292
+ """Sets a table on the query builder
293
+
294
+ Arguments:
295
+ table {string} -- The name of the table
296
+
297
+ Returns:
298
+ self
299
+ """
300
+ return getattr(self.owner, key)
301
+
302
+ def set_scope(self, name, callable):
303
+ """Sets a scope based on a class and maps it to a name.
304
+
305
+ Arguments:
306
+ cls {masoniteorm.Model} -- An ORM model class.
307
+ name {string} -- The name of the scope to use.
308
+
309
+ Returns:
310
+ self
311
+ """
312
+ # setattr(self, name, callable)
313
+ self._scopes.update({name: callable})
314
+
315
+ return self
316
+
317
+ def set_global_scope(self, name="", callable=None, action="select"):
318
+ """Sets the global scopes that should be used before creating the SQL.
319
+
320
+ Arguments:
321
+ cls {masoniteorm.Model} -- An ORM model class.
322
+ name {string} -- The name of the global scope.
323
+
324
+ Returns:
325
+ self
326
+ """
327
+ if isinstance(name, BaseScope):
328
+ name.on_boot(self)
329
+ return self
330
+
331
+ if action not in self._global_scopes:
332
+ self._global_scopes[action] = {}
333
+
334
+ self._global_scopes[action].update({name: callable})
335
+
336
+ return self
337
+
338
+ def without_global_scopes(self):
339
+ self._global_scopes = {}
340
+ return self
341
+
342
+ def remove_global_scope(self, scope, action=None):
343
+ """Sets the global scopes that should be used before creating the SQL.
344
+
345
+ Arguments:
346
+ cls {masoniteorm.Model} -- An ORM model class.
347
+ name {string} -- The name of the global scope.
348
+
349
+ Returns:
350
+ self
351
+ """
352
+ if isinstance(scope, BaseScope):
353
+ scope.on_remove(self)
354
+ return self
355
+
356
+ del self._global_scopes.get(action, {})[scope]
357
+
358
+ return self
359
+
360
+ def __getattr__(self, attribute):
361
+ """Magic method for fetching query scopes.
362
+
363
+ This method is only used when a method or attribute does not already exist.
364
+
365
+ Arguments:
366
+ attribute {string} -- The attribute to fetch.
367
+
368
+ Raises:
369
+ AttributeError: Raised when there is no attribute or scope on the builder class.
370
+
371
+ Returns:
372
+ self
373
+ """
374
+ if attribute == "__setstate__":
375
+ raise AttributeError(
376
+ "'QueryBuilder' object has no attribute '{}'".format(attribute)
377
+ )
378
+
379
+ if attribute in self._scopes:
380
+
381
+ def method(*args, **kwargs):
382
+ return self._scopes[attribute](
383
+ self._model, self, *args, **kwargs
384
+ )
385
+
386
+ return method
387
+
388
+ if attribute in self._macros:
389
+
390
+ def method(*args, **kwargs):
391
+ return self._macros[attribute](
392
+ self._model, self, *args, **kwargs
393
+ )
394
+
395
+ return method
396
+
397
+ raise AttributeError(
398
+ "'QueryBuilder' object has no attribute '{}'".format(attribute)
399
+ )
400
+
401
+ def on(self, connection):
402
+ if connection == "default":
403
+ self.connection = self._connection_details.get("default")
404
+ else:
405
+ self.connection = connection
406
+
407
+ if self.connection not in self._connection_details:
408
+ raise ConnectionNotRegistered(
409
+ f"Could not find the '{self.connection}' connection details"
410
+ )
411
+
412
+ self._connection_driver = self._connection_details.get(
413
+ self.connection
414
+ ).get("driver")
415
+ resolver = ConnectionResolver(
416
+ connection_details=self._connection_details
417
+ )
418
+ self.connection_class = resolver.connection_factory.make(
419
+ self._connection_driver
420
+ )
421
+
422
+ self.grammar = self.connection_class.get_default_query_grammar()
423
+
424
+ return self
425
+
426
+ def select(self, *args):
427
+ """Specifies columns that should be selected
428
+
429
+ Returns:
430
+ self
431
+ """
432
+ for arg in args:
433
+ if isinstance(arg, list):
434
+ for column in arg:
435
+ self._columns += (SelectExpression(column),)
436
+ else:
437
+ for column in arg.split(","):
438
+ self._columns += (SelectExpression(column),)
439
+
440
+ return self
441
+
442
+ def distinct(self, boolean=True):
443
+ """Specifies that all columns should be distinct
444
+
445
+ Returns:
446
+ self
447
+ """
448
+ self._distinct = boolean
449
+ return self
450
+
451
+ def add_select(self, alias, callable):
452
+ """Specifies columns that should be selected
453
+
454
+ Returns:
455
+ self
456
+ """
457
+ builder = callable(self.new())
458
+ self._columns += (SubGroupExpression(builder, alias=alias),)
459
+
460
+ return self
461
+
462
+ def statement(self, query, bindings=None):
463
+ if bindings is None:
464
+ bindings = []
465
+ result = self.new_connection().query(query, bindings)
466
+ return self.prepare_result(result)
467
+
468
+ def select_raw(self, query):
469
+ """Specifies raw SQL that should be injected into the select expression.
470
+
471
+ Returns:
472
+ self
473
+ """
474
+ self._columns += (SelectExpression(query, raw=True),)
475
+ return self
476
+
477
+ def get_processor(self):
478
+ return self.connection_class.get_default_post_processor()()
479
+
480
+ def bulk_create(
481
+ self,
482
+ creates: List[Dict[str, Any]],
483
+ query: bool = False,
484
+ cast: bool = True,
485
+ ):
486
+ self.set_action("bulk_create")
487
+ model: Model = None
488
+
489
+ if self._model:
490
+ model = self._model
491
+
492
+ self._creates = []
493
+ for unsorted_create in creates:
494
+ if model:
495
+ unsorted_create = model.filter_mass_assignment(unsorted_create)
496
+ if cast:
497
+ unsorted_create = model.cast_values(unsorted_create)
498
+ # sort the dicts by key so the values inserted align with the correct column
499
+ self._creates.append(dict(sorted(unsorted_create.items())))
500
+
501
+ if query:
502
+ return self
503
+
504
+ if model:
505
+ model = model.hydrate(self._creates)
506
+ if not self.dry:
507
+ connection = self.new_connection()
508
+ query_result = connection.query(
509
+ self.to_qmark(), self._bindings, results=1
510
+ )
511
+
512
+ processed_results = query_result or self._creates
513
+ else:
514
+ processed_results = self._creates
515
+
516
+ if model:
517
+ return model
518
+
519
+ return processed_results
520
+
521
+ def create(
522
+ self,
523
+ creates: Optional[Dict[str, Any]] = None,
524
+ query: bool = False,
525
+ id_key: str = "id",
526
+ cast: bool = True,
527
+ ignore_mass_assignment: bool = False,
528
+ **kwargs,
529
+ ):
530
+ """Specifies a dictionary that should be used to create new values.
531
+
532
+ Arguments:
533
+ creates {dict} -- A dictionary of columns and values.
534
+
535
+ Returns:
536
+ self
537
+ """
538
+ self.set_action("insert")
539
+ model = None
540
+ self._creates = creates if creates else kwargs
541
+
542
+ if self._model:
543
+ model = self._model
544
+ # Update values with related record's
545
+ self._creates.update(self._creates_related)
546
+ # Filter __fillable/__guarded__ fields
547
+ if not ignore_mass_assignment:
548
+ self._creates = model.filter_mass_assignment(self._creates)
549
+ # Cast values if necessary
550
+ if cast:
551
+ self._creates = model.cast_values(self._creates)
552
+
553
+ if query:
554
+ return self
555
+
556
+ if model:
557
+ model = model.hydrate(self._creates)
558
+ self.observe_events(model, "creating")
559
+
560
+ # if attributes were modified during model observer then we need to update the creates here
561
+ self._creates.update(model.get_dirty_attributes())
562
+
563
+ if not self.dry:
564
+ connection = self.new_connection()
565
+
566
+ query_result = connection.query(
567
+ self.to_qmark(), self._bindings, results=1
568
+ )
569
+
570
+ if model:
571
+ id_key = model.get_primary_key()
572
+
573
+ processed_results = self.get_processor().process_insert_get_id(
574
+ self, query_result or self._creates, id_key
575
+ )
576
+ else:
577
+ processed_results = self._creates
578
+
579
+ if model:
580
+ model = model.fill(processed_results)
581
+ self.observe_events(model, "created")
582
+ return model
583
+
584
+ return processed_results
585
+
586
+ def hydrate(self, result, relations=None):
587
+ return self._model.hydrate(result, relations)
588
+
589
+ def delete(self, column=None, value=None, query=False):
590
+ """Specify the column and value to delete
591
+ or deletes everything based on a previously used where expression.
592
+
593
+ Keyword Arguments:
594
+ column {string} -- The name of the column (default: {None})
595
+ value {string|int} -- The value of the column (default: {None})
596
+
597
+ Returns:
598
+ self
599
+ """
600
+ model = None
601
+ self.set_action("delete")
602
+
603
+ if self._model:
604
+ model = self._model
605
+
606
+ if column and value:
607
+ if isinstance(value, (list, tuple)):
608
+ self.where_in(column, value)
609
+ else:
610
+ self.where(column, value)
611
+
612
+ if query:
613
+ return self
614
+
615
+ if model and model.is_loaded():
616
+ self.where(model.get_primary_key(), model.get_primary_key_value())
617
+ self.observe_events(model, "deleting")
618
+
619
+ connection = self.new_connection()
620
+
621
+ connection.query(self.to_qmark(), self._bindings)
622
+
623
+ if model:
624
+ self.observe_events(model, "deleted")
625
+
626
+ return connection.get_row_count()
627
+
628
+ def where(self, column, *args):
629
+ """Specifies a where expression.
630
+
631
+ Arguments:
632
+ column {string} -- The name of the column to search
633
+
634
+ Keyword Arguments:
635
+ args {List} -- The operator and the value of the column to search. (default: {None})
636
+
637
+ Returns:
638
+ self
639
+ """
640
+ operator, value = self._extract_operator_value(*args)
641
+
642
+ if inspect.isfunction(column):
643
+ builder = column(self.new())
644
+ self._wheres += (
645
+ (QueryExpression(None, operator, SubGroupExpression(builder))),
646
+ )
647
+ elif isinstance(column, dict):
648
+ for key, value in column.items():
649
+ self._wheres += ((QueryExpression(key, "=", value, "value")),)
650
+ elif isinstance(value, QueryBuilder):
651
+ self._wheres += (
652
+ (
653
+ QueryExpression(
654
+ column, operator, SubSelectExpression(value)
655
+ )
656
+ ),
657
+ )
658
+ else:
659
+ self._wheres += (
660
+ (QueryExpression(column, operator, value, "value")),
661
+ )
662
+ return self
663
+
664
+ def where_from_builder(self, builder):
665
+ """Specifies a where expression.
666
+
667
+ Arguments:
668
+ column {string} -- The name of the column to search
669
+
670
+ Keyword Arguments:
671
+ args {List} -- The operator and the value of the column to search. (default: {None})
672
+
673
+ Returns:
674
+ self
675
+ """
676
+
677
+ self._wheres += (
678
+ (QueryExpression(None, "=", SubGroupExpression(builder))),
679
+ )
680
+
681
+ return self
682
+
683
+ def where_like(self, column, value):
684
+ """Specifies a where expression.
685
+
686
+ Arguments:
687
+ column {string} -- The name of the column to search
688
+
689
+ Keyword Arguments:
690
+ args {List} -- The operator and the value of the column to search. (default: {None})
691
+
692
+ Returns:
693
+ self
694
+ """
695
+ return self.where(column, "like", value)
696
+
697
+ def where_not_like(self, column, value):
698
+ """Specifies a where expression.
699
+
700
+ Arguments:
701
+ column {string} -- The name of the column to search
702
+
703
+ Keyword Arguments:
704
+ args {List} -- The operator and the value of the column to search. (default: {None})
705
+
706
+ Returns:
707
+ self
708
+ """
709
+ return self.where(column, "not like", value)
710
+
711
+ def where_raw(self, query: str, bindings=()):
712
+ """Specifies raw SQL that should be injected into the where expression.
713
+
714
+ Arguments:
715
+ query {string} -- The raw query string.
716
+
717
+ Keyword Arguments:
718
+ bindings {tuple} -- query bindings that should be added to the connection. (default: {()})
719
+
720
+ Returns:
721
+ self
722
+ """
723
+ self._wheres += (
724
+ (
725
+ QueryExpression(
726
+ query, "=", None, "value", raw=True, bindings=bindings
727
+ )
728
+ ),
729
+ )
730
+ return self
731
+
732
+ def or_where(self, column, *args):
733
+ """Specifies an or where query expression.
734
+
735
+ Arguments:
736
+ column {[type]} -- [description]
737
+ value {[type]} -- [description]
738
+
739
+ Returns:
740
+ [type] -- [description]
741
+ """
742
+ operator, value = self._extract_operator_value(*args)
743
+ if inspect.isfunction(column):
744
+ builder = column(self.new())
745
+ self._wheres += (
746
+ (
747
+ QueryExpression(
748
+ None,
749
+ operator,
750
+ SubGroupExpression(builder),
751
+ keyword="or",
752
+ )
753
+ ),
754
+ )
755
+ elif isinstance(value, QueryBuilder):
756
+ self._wheres += (
757
+ (
758
+ QueryExpression(
759
+ column, operator, SubSelectExpression(value)
760
+ )
761
+ ),
762
+ )
763
+ else:
764
+ self._wheres += (
765
+ (
766
+ QueryExpression(
767
+ column, operator, value, "value", keyword="or"
768
+ )
769
+ ),
770
+ )
771
+ return self
772
+
773
+ def where_exists(self, value: "str|int|QueryBuilder"):
774
+ """Specifies a where exists expression.
775
+
776
+ Arguments:
777
+ value {string|int|QueryBuilder} -- A value to check for the existence of a query expression.
778
+
779
+ Returns:
780
+ self
781
+ """
782
+ if inspect.isfunction(value):
783
+ self._wheres += (
784
+ (
785
+ QueryExpression(
786
+ None, "EXISTS", SubSelectExpression(value(self.new()))
787
+ )
788
+ ),
789
+ )
790
+ elif isinstance(value, QueryBuilder):
791
+ self._wheres += (
792
+ (QueryExpression(None, "EXISTS", SubSelectExpression(value))),
793
+ )
794
+ else:
795
+ self._wheres += (
796
+ (QueryExpression(None, "EXISTS", value, "value")),
797
+ )
798
+
799
+ return self
800
+
801
+ def or_where_exists(self, value: "str|int|QueryBuilder"):
802
+ """Specifies a where exists expression.
803
+
804
+ Arguments:
805
+ value {string|int|QueryBuilder} -- A value to check for the existence of a query expression.
806
+
807
+ Returns:
808
+ self
809
+ """
810
+ if inspect.isfunction(value):
811
+ self._wheres += (
812
+ (
813
+ QueryExpression(
814
+ None,
815
+ "EXISTS",
816
+ SubSelectExpression(value(self.new())),
817
+ keyword="or",
818
+ )
819
+ ),
820
+ )
821
+ elif isinstance(value, QueryBuilder):
822
+ self._wheres += (
823
+ (
824
+ QueryExpression(
825
+ None,
826
+ "EXISTS",
827
+ SubSelectExpression(value),
828
+ keyword="or",
829
+ )
830
+ ),
831
+ )
832
+ else:
833
+ self._wheres += (
834
+ (
835
+ QueryExpression(
836
+ None, "EXISTS", value, "value", keyword="or"
837
+ )
838
+ ),
839
+ )
840
+
841
+ return self
842
+
843
+ def where_not_exists(self, value: "str|int|QueryBuilder"):
844
+ """Specifies a where exists expression.
845
+
846
+ Arguments:
847
+ value {string|int|QueryBuilder} -- A value to check for the existence of a query expression.
848
+
849
+ Returns:
850
+ self
851
+ """
852
+
853
+ if inspect.isfunction(value):
854
+ self._wheres += (
855
+ (
856
+ QueryExpression(
857
+ None,
858
+ "NOT EXISTS",
859
+ SubSelectExpression(value(self.new())),
860
+ )
861
+ ),
862
+ )
863
+ elif isinstance(value, QueryBuilder):
864
+ self._wheres += (
865
+ (
866
+ QueryExpression(
867
+ None, "NOT EXISTS", SubSelectExpression(value)
868
+ )
869
+ ),
870
+ )
871
+ else:
872
+ self._wheres += (
873
+ (QueryExpression(None, "NOT EXISTS", value, "value")),
874
+ )
875
+
876
+ return self
877
+
878
+ def or_where_not_exists(self, value: "str|int|QueryBuilder"):
879
+ """Specifies a where exists expression.
880
+
881
+ Arguments:
882
+ value {string|int|QueryBuilder} -- A value to check for the existence of a query expression.
883
+
884
+ Returns:
885
+ self
886
+ """
887
+
888
+ if inspect.isfunction(value):
889
+ self._wheres += (
890
+ (
891
+ QueryExpression(
892
+ None,
893
+ "NOT EXISTS",
894
+ SubSelectExpression(value(self.new())),
895
+ keyword="or",
896
+ )
897
+ ),
898
+ )
899
+ elif isinstance(value, QueryBuilder):
900
+ self._wheres += (
901
+ (
902
+ QueryExpression(
903
+ None,
904
+ "NOT EXISTS",
905
+ SubSelectExpression(value),
906
+ keyword="or",
907
+ )
908
+ ),
909
+ )
910
+ else:
911
+ self._wheres += (
912
+ (
913
+ QueryExpression(
914
+ None, "NOT EXISTS", value, "value", keyword="or"
915
+ )
916
+ ),
917
+ )
918
+
919
+ return self
920
+
921
+ def having(self, column, equality="", value=""):
922
+ """Specifying a having expression.
923
+
924
+ Arguments:
925
+ column {string} -- The name of the column.
926
+
927
+ Keyword Arguments:
928
+ equality {string} -- An equality operator (default: {"="})
929
+ value {string} -- The value of the having expression (default: {""})
930
+
931
+ Returns:
932
+ self
933
+ """
934
+ self._having += ((HavingExpression(column, equality, value)),)
935
+ return self
936
+
937
+ def having_raw(self, string):
938
+ """Specifies raw SQL that should be injected into the having expression.
939
+
940
+ Arguments:
941
+ string {string} -- The raw query string.
942
+
943
+ Returns:
944
+ self
945
+ """
946
+ self._having += ((HavingExpression(string, raw=True)),)
947
+ return self
948
+
949
+ def where_null(self, column):
950
+ """Specifies a where expression where the column is NULL.
951
+
952
+ Arguments:
953
+ column {string} -- The name of the column.
954
+
955
+ Returns:
956
+ self
957
+ """
958
+ self._wheres += ((QueryExpression(column, "=", None, "NULL")),)
959
+ return self
960
+
961
+ def or_where_null(self, column):
962
+ """Specifies a where expression where the column is NULL.
963
+
964
+ Arguments:
965
+ column {string} -- The name of the column.
966
+
967
+ Returns:
968
+ self
969
+ """
970
+ self._wheres += (
971
+ (QueryExpression(column, "=", None, "NULL", keyword="or")),
972
+ )
973
+ return self
974
+
975
+ def chunk(self, chunk_amount):
976
+ chunk_connection = self.new_connection()
977
+ for result in chunk_connection.select_many(
978
+ self.to_sql(), (), chunk_amount
979
+ ):
980
+ yield self.prepare_result(result)
981
+
982
+ def where_not_null(self, column: str):
983
+ """Specifies a where expression where the column is not NULL.
984
+
985
+ Arguments:
986
+ column {string} -- The name of the column.
987
+
988
+ Returns:
989
+ self
990
+ """
991
+ self._wheres += ((QueryExpression(column, "=", True, "NOT NULL")),)
992
+ return self
993
+
994
+ def _get_date_string(self, date):
995
+ if isinstance(date, str):
996
+ return date
997
+ elif hasattr(date, "to_date_string"):
998
+ return date.to_date_string()
999
+ elif hasattr(date, "strftime"):
1000
+ return date.strftime("%m-%d-%Y")
1001
+
1002
+ def where_date(self, column: str, date: "str|datetime"):
1003
+ """Specifies a where DATE expression
1004
+
1005
+ Arguments:
1006
+ column {string} -- The name of the column.
1007
+
1008
+ Returns:
1009
+ self
1010
+ """
1011
+ self._wheres += (
1012
+ (
1013
+ QueryExpression(
1014
+ column, "=", self._get_date_string(date), "DATE"
1015
+ )
1016
+ ),
1017
+ )
1018
+ return self
1019
+
1020
+ def or_where_date(self, column: str, date: "str|datetime"):
1021
+ """Specifies a where DATE expression
1022
+
1023
+ Arguments:
1024
+ column {string} -- The name of the column.
1025
+ date {string|datetime|pendulum} -- The name of the column.
1026
+
1027
+ Returns:
1028
+ self
1029
+ """
1030
+ self._wheres += (
1031
+ (
1032
+ QueryExpression(
1033
+ column,
1034
+ "=",
1035
+ self._get_date_string(date),
1036
+ "DATE",
1037
+ keyword="or",
1038
+ )
1039
+ ),
1040
+ )
1041
+ return self
1042
+
1043
+ def between(self, column: str, low: int, high: int):
1044
+ """Specifies a where between expression.
1045
+
1046
+ Arguments:
1047
+ column {string} -- The name of the column.
1048
+ low {string} -- The value on the low end.
1049
+ high {string} -- The value on the high end.
1050
+
1051
+ Returns:
1052
+ self
1053
+ """
1054
+ self._wheres += (BetweenExpression(column, low, high),)
1055
+ return self
1056
+
1057
+ def where_between(self, *args, **kwargs):
1058
+ return self.between(*args, **kwargs)
1059
+
1060
+ def where_not_between(self, *args, **kwargs):
1061
+ return self.not_between(*args, **kwargs)
1062
+
1063
+ def not_between(self, column: str, low: str, high: str):
1064
+ """Specifies a where not between expression.
1065
+
1066
+ Arguments:
1067
+ column {string} -- The name of the column.
1068
+ low {string} -- The value on the low end.
1069
+ high {string} -- The value on the high end.
1070
+
1071
+ Returns:
1072
+ self
1073
+ """
1074
+ self._wheres += (
1075
+ BetweenExpression(column, low, high, equality="NOT BETWEEN"),
1076
+ )
1077
+ return self
1078
+
1079
+ def where_in(self, column, wheres=None):
1080
+ """Specifies where a column contains a list of a values.
1081
+
1082
+ Arguments:
1083
+ column {string} -- The name of the column.
1084
+
1085
+ Keyword Arguments:
1086
+ wheres {list} -- A list of values (default: {[]})
1087
+
1088
+ Returns:
1089
+ self
1090
+ """
1091
+
1092
+ wheres = wheres or []
1093
+
1094
+ if not wheres:
1095
+ self._wheres += ((QueryExpression(0, "=", 1, "value_equals")),)
1096
+
1097
+ elif isinstance(wheres, QueryBuilder):
1098
+ self._wheres += (
1099
+ (QueryExpression(column, "IN", SubSelectExpression(wheres))),
1100
+ )
1101
+ elif callable(wheres):
1102
+ self._wheres += (
1103
+ (
1104
+ QueryExpression(
1105
+ column, "IN", SubSelectExpression(wheres(self.new()))
1106
+ )
1107
+ ),
1108
+ )
1109
+ else:
1110
+ self._wheres += ((QueryExpression(column, "IN", list(wheres))),)
1111
+ return self
1112
+
1113
+ def get_relation(self, relationship, builder=None):
1114
+ if not builder:
1115
+ builder = self
1116
+
1117
+ if not builder._model:
1118
+ raise AttributeError(
1119
+ "You must specify a model in order to use relationship methods"
1120
+ )
1121
+
1122
+ return getattr(builder._model, relationship)
1123
+
1124
+ def has(self, *relationships):
1125
+ if not self._model:
1126
+ raise AttributeError(
1127
+ "You must specify a model in order to use 'has' relationship methods"
1128
+ )
1129
+
1130
+ for relationship in relationships:
1131
+ if "." in relationship:
1132
+ last_builder = self._model.builder
1133
+ for split_relationship in relationship.split("."):
1134
+ related = last_builder.get_relation(split_relationship)
1135
+ last_builder = related.query_has(last_builder)
1136
+ else:
1137
+ related = getattr(self._model, relationship)
1138
+ related.query_has(self)
1139
+ return self
1140
+
1141
+ def or_has(self, *relationships):
1142
+ if not self._model:
1143
+ raise AttributeError(
1144
+ "You must specify a model in order to use 'has' relationship methods"
1145
+ )
1146
+
1147
+ for relationship in relationships:
1148
+ if "." in relationship:
1149
+ last_builder = self._model.builder
1150
+ split_count = len(relationship.split("."))
1151
+ for index, split_relationship in enumerate(
1152
+ relationship.split(".")
1153
+ ):
1154
+ related = last_builder.get_relation(split_relationship)
1155
+
1156
+ if index + 1 == split_count:
1157
+ last_builder = related.query_has(
1158
+ last_builder, method="where_exists"
1159
+ )
1160
+ continue
1161
+
1162
+ last_builder = related.query_has(
1163
+ last_builder, method="or_where_exists"
1164
+ )
1165
+ else:
1166
+ related = getattr(self._model, relationship)
1167
+ related.query_has(self, method="or_where_exists")
1168
+ return self
1169
+
1170
+ def doesnt_have(self, *relationships):
1171
+ if not self._model:
1172
+ raise AttributeError(
1173
+ "You must specify a model in order to use the 'doesnt_have' relationship methods"
1174
+ )
1175
+
1176
+ for relationship in relationships:
1177
+ if "." in relationship:
1178
+ last_builder = self._model.builder
1179
+ split_count = len(relationship.split("."))
1180
+ for index, split_relationship in enumerate(
1181
+ relationship.split(".")
1182
+ ):
1183
+ related = last_builder.get_relation(split_relationship)
1184
+ if index + 1 == split_count:
1185
+ last_builder = related.query_has(
1186
+ last_builder, method="where_exists"
1187
+ )
1188
+ continue
1189
+
1190
+ last_builder = related.query_has(
1191
+ last_builder, method="where_not_exists"
1192
+ )
1193
+ else:
1194
+ related = getattr(self._model, relationship)
1195
+ related.query_has(self, method="where_not_exists")
1196
+ return self
1197
+
1198
+ def or_doesnt_have(self, *relationships):
1199
+ if not self._model:
1200
+ raise AttributeError(
1201
+ "You must specify a model in order to use the 'doesnt_have' relationship methods"
1202
+ )
1203
+
1204
+ for relationship in relationships:
1205
+ if "." in relationship:
1206
+ last_builder = self._model.builder
1207
+ split_count = len(relationship.split("."))
1208
+ for index, split_relationship in enumerate(
1209
+ relationship.split(".")
1210
+ ):
1211
+ related = last_builder.get_relation(split_relationship)
1212
+ if index + 1 == split_count:
1213
+ last_builder = related.query_has(
1214
+ last_builder, method="where_exists"
1215
+ )
1216
+ continue
1217
+
1218
+ last_builder = related.query_has(
1219
+ last_builder, method="or_where_not_exists"
1220
+ )
1221
+ else:
1222
+ related = getattr(self._model, relationship)
1223
+ related.query_has(self, method="or_where_not_exists")
1224
+ return self
1225
+
1226
+ def where_has(self, relationship, callback):
1227
+ if not self._model:
1228
+ raise AttributeError(
1229
+ "You must specify a model in order to use 'has' relationship methods"
1230
+ )
1231
+
1232
+ if "." in relationship:
1233
+ last_builder = self._model.builder
1234
+ splits = relationship.split(".")
1235
+ split_count = len(splits)
1236
+ for index, split_relationship in enumerate(splits):
1237
+ related = last_builder.get_relation(split_relationship)
1238
+
1239
+ if index + 1 == split_count:
1240
+ last_builder = related.query_where_exists(
1241
+ last_builder, callback, method="where_exists"
1242
+ )
1243
+ continue
1244
+ last_builder = related.query_has(
1245
+ last_builder, method="where_exists"
1246
+ )
1247
+ else:
1248
+ related = getattr(self._model, relationship)
1249
+ related.query_where_exists(self, callback, method="where_exists")
1250
+ return self
1251
+
1252
+ def or_where_has(self, relationship, callback):
1253
+ if not self._model:
1254
+ raise AttributeError(
1255
+ "You must specify a model in order to use 'has' relationship methods"
1256
+ )
1257
+
1258
+ if "." in relationship:
1259
+ last_builder = self._model.builder
1260
+ splits = relationship.split(".")
1261
+ split_count = len(splits)
1262
+ for index, split_relationship in enumerate(splits):
1263
+ related = last_builder.get_relation(split_relationship)
1264
+ if index + 1 == split_count:
1265
+ last_builder = related.query_where_exists(
1266
+ last_builder, callback, method="where_exists"
1267
+ )
1268
+ continue
1269
+
1270
+ last_builder = related.query_has(
1271
+ last_builder, method="or_where_exists"
1272
+ )
1273
+ else:
1274
+ related = getattr(self._model, relationship)
1275
+ related.query_where_exists(
1276
+ self, callback, method="or_where_exists"
1277
+ )
1278
+ return self
1279
+
1280
+ def where_doesnt_have(self, relationship, callback):
1281
+ if not self._model:
1282
+ raise AttributeError(
1283
+ "You must specify a model in order to use the 'doesnt_have' relationship methods"
1284
+ )
1285
+
1286
+ if "." in relationship:
1287
+ last_builder = self._model.builder
1288
+ split_count = len(relationship.split("."))
1289
+ for index, split_relationship in enumerate(
1290
+ relationship.split(".")
1291
+ ):
1292
+ related = last_builder.get_relation(split_relationship)
1293
+ if index + 1 == split_count:
1294
+ last_builder = getattr(
1295
+ last_builder._model, split_relationship
1296
+ ).query_where_exists(self, callback, method="where_exists")
1297
+ continue
1298
+
1299
+ last_builder = related.query_has(
1300
+ last_builder, method="where_not_exists"
1301
+ )
1302
+ else:
1303
+ related = getattr(self._model, relationship)
1304
+ related.query_where_exists(
1305
+ self, callback, method="where_not_exists"
1306
+ )
1307
+ return self
1308
+
1309
+ def or_where_doesnt_have(self, relationship, callback):
1310
+ if not self._model:
1311
+ raise AttributeError(
1312
+ "You must specify a model in order to use the 'doesnt_have' relationship methods"
1313
+ )
1314
+
1315
+ if "." in relationship:
1316
+ last_builder = self._model.builder
1317
+ split_count = len(relationship.split("."))
1318
+ for index, split_relationship in enumerate(
1319
+ relationship.split(".")
1320
+ ):
1321
+ related = last_builder.get_relation(split_relationship)
1322
+ if index + 1 == split_count:
1323
+ last_builder = getattr(
1324
+ last_builder._model, split_relationship
1325
+ ).query_where_exists(self, callback, method="where_exists")
1326
+ continue
1327
+
1328
+ last_builder = related.query_has(
1329
+ last_builder, method="or_where_not_exists"
1330
+ )
1331
+ else:
1332
+ related = getattr(self._model, relationship)
1333
+ related.query_where_exists(
1334
+ self, callback, method="or_where_not_exists"
1335
+ )
1336
+ return self
1337
+
1338
+ def with_count(self, relationship, callback=None):
1339
+ self.select(*self._model.get_selects())
1340
+ return getattr(self._model, relationship).get_with_count_query(
1341
+ self, callback=callback
1342
+ )
1343
+
1344
+ def where_not_in(self, column, wheres=None):
1345
+ """Specifies where a column does not contain a list of a values.
1346
+
1347
+ Arguments:
1348
+ column {string} -- The name of the column.
1349
+
1350
+ Keyword Arguments:
1351
+ wheres {list} -- A list of values (default: {[]})
1352
+
1353
+ Returns:
1354
+ self
1355
+ """
1356
+
1357
+ wheres = wheres or []
1358
+
1359
+ if isinstance(wheres, QueryBuilder):
1360
+ self._wheres += (
1361
+ (
1362
+ QueryExpression(
1363
+ column, "NOT IN", SubSelectExpression(wheres)
1364
+ )
1365
+ ),
1366
+ )
1367
+ else:
1368
+ self._wheres += (
1369
+ (QueryExpression(column, "NOT IN", list(wheres))),
1370
+ )
1371
+ return self
1372
+
1373
+ def join(
1374
+ self,
1375
+ table: str,
1376
+ column1=None,
1377
+ equality=None,
1378
+ column2=None,
1379
+ clause="inner",
1380
+ ):
1381
+ """Specifies a join expression.
1382
+
1383
+ Arguments:
1384
+ table {string} -- The name of the table or an instance of JoinClause.
1385
+ column1 {string} -- The name of the foreign table.
1386
+ equality {string} -- The equality to join on.
1387
+ column2 {string} -- The name of the local column.
1388
+
1389
+ Keyword Arguments:
1390
+ clause {string} -- The action clause. (default: {"inner"})
1391
+
1392
+ Returns:
1393
+ self
1394
+ """
1395
+ if inspect.isfunction(column1):
1396
+ self._joins += (column1(JoinClause(table, clause=clause)),)
1397
+ elif isinstance(table, str):
1398
+ self._joins += (
1399
+ JoinClause(table, clause=clause).on(
1400
+ column1, equality, column2
1401
+ ),
1402
+ )
1403
+ else:
1404
+ self._joins += (table,)
1405
+ return self
1406
+
1407
+ def left_join(self, table, column1=None, equality=None, column2=None):
1408
+ """A helper method to add a left join expression.
1409
+
1410
+ Arguments:
1411
+ table {string} -- The name of the table to join on.
1412
+ column1 {string} -- The name of the foreign table.
1413
+ equality {string} -- The equality to join on.
1414
+ column2 {string} -- The name of the local column.
1415
+
1416
+ Returns:
1417
+ self
1418
+ """
1419
+ return self.join(
1420
+ table=table,
1421
+ column1=column1,
1422
+ equality=equality,
1423
+ column2=column2,
1424
+ clause="left",
1425
+ )
1426
+
1427
+ def right_join(self, table, column1=None, equality=None, column2=None):
1428
+ """A helper method to add a right join expression.
1429
+
1430
+ Arguments:
1431
+ table {string} -- The name of the table to join on.
1432
+ column1 {string} -- The name of the foreign table.
1433
+ equality {string} -- The equality to join on.
1434
+ column2 {string} -- The name of the local column.
1435
+
1436
+ Returns:
1437
+ self
1438
+ """
1439
+ return self.join(
1440
+ table=table,
1441
+ column1=column1,
1442
+ equality=equality,
1443
+ column2=column2,
1444
+ clause="right",
1445
+ )
1446
+
1447
+ def joins(self, *relationships, clause="inner"):
1448
+ for relationship in relationships:
1449
+ getattr(self._model, relationship).joins(self, clause=clause)
1450
+
1451
+ return self
1452
+
1453
+ def join_on(self, relationship, callback=None, clause="inner"):
1454
+ relation = getattr(self._model, relationship)
1455
+ relation.joins(self, clause=clause)
1456
+
1457
+ if callback:
1458
+ new_from_builder = self.new_from_builder()
1459
+ new_from_builder.table(relation.get_builder().get_table_name())
1460
+ self.where_from_builder(callback(new_from_builder))
1461
+
1462
+ return self
1463
+
1464
+ def where_column(self, column1, column2):
1465
+ """Specifies where two columns equal eachother.
1466
+
1467
+ Arguments:
1468
+ column1 {string} -- The name of the column.
1469
+ column2 {string} -- The name of the column.
1470
+
1471
+ Returns:
1472
+ self
1473
+ """
1474
+ self._wheres += ((QueryExpression(column1, "=", column2, "column")),)
1475
+ return self
1476
+
1477
+ def take(self, *args, **kwargs):
1478
+ """Alias for limit method"""
1479
+ return self.limit(*args, **kwargs)
1480
+
1481
+ def limit(self, amount):
1482
+ """Specifies a limit expression.
1483
+
1484
+ Arguments:
1485
+ amount {int} -- The number of rows to limit.
1486
+
1487
+ Returns:
1488
+ self
1489
+ """
1490
+ self._limit = amount
1491
+ return self
1492
+
1493
+ def offset(self, amount):
1494
+ """Specifies an offset expression.
1495
+
1496
+ Arguments:
1497
+ amount {int} -- The number of rows to limit.
1498
+
1499
+ Returns:
1500
+ self
1501
+ """
1502
+ self._offset = amount
1503
+ return self
1504
+
1505
+ def skip(self, *args, **kwargs):
1506
+ """Alias for limit method"""
1507
+ return self.offset(*args, **kwargs)
1508
+
1509
+ def update(
1510
+ self,
1511
+ updates: Dict[str, Any],
1512
+ dry: bool = False,
1513
+ force: bool = False,
1514
+ cast: bool = True,
1515
+ ignore_mass_assignment: bool = False,
1516
+ ):
1517
+ """Specifies columns and values to be updated.
1518
+
1519
+ Arguments:
1520
+ updates {dictionary} -- A dictionary of columns and values to update.
1521
+ dry {bool, optional}: Do everything except execute the query against the DB
1522
+ force {bool, optional}: Force an update statement to be executed even if nothing was changed
1523
+ cast {bool, optional}: Run all values through model's casters
1524
+ ignore_mass_assignment {bool, optional}: Whether the update should ignore mass assignment on the model
1525
+
1526
+ Returns:
1527
+ self
1528
+ """
1529
+ model: Model = None
1530
+
1531
+ additional = {}
1532
+
1533
+ if self._model:
1534
+ model = self._model
1535
+ # Filter __fillable/__guarded__ fields
1536
+ if not ignore_mass_assignment:
1537
+ updates = model.filter_mass_assignment(updates)
1538
+
1539
+ if model and model.is_loaded():
1540
+ self.where(model.get_primary_key(), model.get_primary_key_value())
1541
+ additional.update(
1542
+ {model.get_primary_key(): model.get_primary_key_value()}
1543
+ )
1544
+
1545
+ self.observe_events(model, "updating")
1546
+
1547
+ if model:
1548
+ if not model.__force_update__ and not force:
1549
+ # Filter updates to only those with changes
1550
+ updates = {
1551
+ attr: value
1552
+ for attr, value in updates.items()
1553
+ if (
1554
+ value is None
1555
+ or model.__original_attributes__.get(attr, None)
1556
+ != value
1557
+ )
1558
+ }
1559
+
1560
+ # Do not execute query if no changes
1561
+ if not updates:
1562
+ return self if dry or self.dry else model
1563
+
1564
+ if cast:
1565
+ updates = model.cast_values(updates)
1566
+
1567
+ if not updates:
1568
+ # Do not perform query if there are no updates
1569
+ return self
1570
+
1571
+ self._updates = (UpdateQueryExpression(updates),)
1572
+ self.set_action("update")
1573
+ if dry or self.dry:
1574
+ return self
1575
+
1576
+ additional.update(updates)
1577
+ connection = self.new_connection()
1578
+
1579
+ connection.query(self.to_qmark(), self._bindings)
1580
+ if model:
1581
+ model.fill(updates)
1582
+ self.observe_events(model, "updated")
1583
+ model.fill_original(updates)
1584
+ return connection.get_row_count()
1585
+ return additional
1586
+
1587
+ def force_update(self, updates: dict, dry=False):
1588
+ return self.update(updates, dry=dry, force=True)
1589
+
1590
+ def set_updates(self, updates: dict, dry=False):
1591
+ """Specifies columns and values to be updated.
1592
+
1593
+ Arguments:
1594
+ updates {dictionary} -- A dictionary of columns and values to update.
1595
+
1596
+ Keyword Arguments:
1597
+ dry {bool} -- Whether the query should be executed. (default: {False})
1598
+
1599
+ Returns:
1600
+ self
1601
+ """
1602
+ self._updates += (UpdateQueryExpression(updates),)
1603
+ return self
1604
+
1605
+ def increment(self, column, value=1, dry=False):
1606
+ """Increments a column's value.
1607
+
1608
+ Arguments:
1609
+ column {string} -- The name of the column.
1610
+
1611
+ Keyword Arguments:
1612
+ value {int} -- The value to increment by. (default: {1})
1613
+
1614
+ Returns:
1615
+ self
1616
+ """
1617
+ model = None
1618
+ id_key = "id"
1619
+ id_value = None
1620
+
1621
+ additional = {}
1622
+
1623
+ if self._model:
1624
+ model = self._model
1625
+ id_value = self._model.get_primary_key_value()
1626
+
1627
+ if model and model.is_loaded():
1628
+ self.where(model.get_primary_key(), model.get_primary_key_value())
1629
+ additional.update(
1630
+ {model.get_primary_key(): model.get_primary_key_value()}
1631
+ )
1632
+
1633
+ self.observe_events(model, "updating")
1634
+
1635
+ self._updates += (
1636
+ UpdateQueryExpression(column, value, update_type="increment"),
1637
+ )
1638
+
1639
+ self.set_action("update")
1640
+ if dry or self.dry:
1641
+ return self
1642
+
1643
+ results = self.new_connection().query(self.to_qmark(), self._bindings)
1644
+ processed_results = self.get_processor().get_column_value(
1645
+ self, column, results, id_key, id_value
1646
+ )
1647
+ return processed_results
1648
+
1649
+ def decrement(self, column, value=1, dry=False):
1650
+ """Decrements a column's value.
1651
+
1652
+ Arguments:
1653
+ column {string} -- The name of the column.
1654
+
1655
+ Keyword Arguments:
1656
+ value {int} -- The value to decrement by. (default: {1})
1657
+
1658
+ Returns:
1659
+ self
1660
+ """
1661
+ model = None
1662
+ id_key = "id"
1663
+ id_value = None
1664
+
1665
+ additional = {}
1666
+
1667
+ if self._model:
1668
+ model = self._model
1669
+ id_value = self._model.get_primary_key_value()
1670
+
1671
+ if model and model.is_loaded():
1672
+ self.where(model.get_primary_key(), model.get_primary_key_value())
1673
+ additional.update(
1674
+ {model.get_primary_key(): model.get_primary_key_value()}
1675
+ )
1676
+
1677
+ self.observe_events(model, "updating")
1678
+
1679
+ self._updates += (
1680
+ UpdateQueryExpression(column, value, update_type="decrement"),
1681
+ )
1682
+
1683
+ self.set_action("update")
1684
+ if dry or self.dry:
1685
+ return self
1686
+
1687
+ result = self.new_connection().query(self.to_qmark(), self._bindings)
1688
+ processed_results = self.get_processor().get_column_value(
1689
+ self, column, result, id_key, id_value
1690
+ )
1691
+ return processed_results
1692
+
1693
+ def sum(self, column):
1694
+ """Aggregates a columns values.
1695
+
1696
+ Arguments:
1697
+ column {string} -- The name of the column to aggregate.
1698
+
1699
+ Returns:
1700
+ self
1701
+ """
1702
+ self.aggregate("SUM", "{column}".format(column=column))
1703
+ return self
1704
+
1705
+ def count(self, column=None, dry=False):
1706
+ """Aggregates a columns values.
1707
+
1708
+ Arguments:
1709
+ column {string} -- The name of the column to aggregate.
1710
+
1711
+ Returns:
1712
+ self
1713
+ """
1714
+ alias = (
1715
+ "m_count_reserved" if (column == "*" or column is None) else column
1716
+ )
1717
+ if column == "*":
1718
+ self.aggregate("COUNT", f"{column} as {alias}")
1719
+ elif column is None:
1720
+ self.aggregate("COUNT", f"* as {alias}")
1721
+ else:
1722
+ self.aggregate("COUNT", f"{column}")
1723
+
1724
+ if dry or self.dry:
1725
+ return self
1726
+
1727
+ if not column:
1728
+ result = self.new_connection().query(
1729
+ self.to_qmark(), self._bindings, results=1
1730
+ )
1731
+
1732
+ if isinstance(result, dict):
1733
+ return result.get(alias, 0)
1734
+
1735
+ prepared_result = list(result.values())
1736
+ if not prepared_result:
1737
+ return 0
1738
+ return prepared_result[0]
1739
+ else:
1740
+ return self
1741
+
1742
+ def max(self, column):
1743
+ """Aggregates a columns values.
1744
+
1745
+ Arguments:
1746
+ column {string} -- The name of the column to aggregate.
1747
+
1748
+ Returns:
1749
+ self
1750
+ """
1751
+ self.aggregate("MAX", "{column}".format(column=column))
1752
+ return self
1753
+
1754
+ def order_by(self, column, direction="ASC"):
1755
+ """Specifies a column to order by.
1756
+
1757
+ Arguments:
1758
+ column {string} -- The name of the column.
1759
+
1760
+ Keyword Arguments:
1761
+ direction {string} -- Specify either ASC or DESC order. (default: {"ASC"})
1762
+
1763
+ Returns:
1764
+ self
1765
+ """
1766
+ for col in column.split(","):
1767
+ self._order_by += (OrderByExpression(col, direction=direction),)
1768
+ return self
1769
+
1770
+ def order_by_raw(self, query, bindings=None):
1771
+ """Specifies a column to order by.
1772
+
1773
+ Arguments:
1774
+ column {string} -- The name of the column.
1775
+
1776
+ Keyword Arguments:
1777
+ direction {string} -- Specify either ASC or DESC order. (default: {"ASC"})
1778
+
1779
+ Returns:
1780
+ self
1781
+ """
1782
+ if bindings is None:
1783
+ bindings = []
1784
+ self._order_by += (
1785
+ OrderByExpression(query, raw=True, bindings=bindings),
1786
+ )
1787
+ return self
1788
+
1789
+ def group_by(self, column):
1790
+ """Specifies a column to group by.
1791
+
1792
+ Arguments:
1793
+ column {string} -- The name of the column to group by.
1794
+
1795
+ Returns:
1796
+ self
1797
+ """
1798
+ for col in column.split(","):
1799
+ self._group_by += (GroupByExpression(column=col),)
1800
+
1801
+ return self
1802
+
1803
+ def group_by_raw(self, query, bindings=None):
1804
+ """Specifies a column to group by.
1805
+
1806
+ Arguments:
1807
+ query {string} -- A raw query
1808
+
1809
+ Returns:
1810
+ self
1811
+ """
1812
+ if bindings is None:
1813
+ bindings = []
1814
+ self._group_by += (
1815
+ GroupByExpression(column=query, raw=True, bindings=bindings),
1816
+ )
1817
+
1818
+ return self
1819
+
1820
+ def aggregate(self, aggregate, column, alias=None):
1821
+ """Helper function to aggregate.
1822
+
1823
+ Arguments:
1824
+ aggregate {string} -- The name of the aggregation.
1825
+ column {string} -- The name of the column to aggregate.
1826
+ """
1827
+ self._aggregates += (
1828
+ AggregateExpression(
1829
+ aggregate=aggregate, column=column, alias=alias
1830
+ ),
1831
+ )
1832
+
1833
+ def first(self, fields=None, query=False):
1834
+ """Gets the first record.
1835
+
1836
+ Returns:
1837
+ dictionary -- Returns a dictionary of results.
1838
+ """
1839
+
1840
+ if not fields:
1841
+ fields = []
1842
+
1843
+ self.select(fields).limit(1)
1844
+
1845
+ if query:
1846
+ return self
1847
+
1848
+ result = self.new_connection().query(
1849
+ self.to_qmark(), self._bindings, results=1
1850
+ )
1851
+
1852
+ return self.prepare_result(result)
1853
+
1854
+ def first_or_create(self, wheres, creates: dict = None):
1855
+ """Get the first record matching the attributes or create it.
1856
+
1857
+ Returns:
1858
+ Model
1859
+ """
1860
+ if creates is None:
1861
+ creates = {}
1862
+
1863
+ record = self.where(wheres).first()
1864
+ total = {}
1865
+ if record:
1866
+ if hasattr(record, "serialize"):
1867
+ total.update(record.serialize())
1868
+ else:
1869
+ total.update(record)
1870
+
1871
+ total.update(creates)
1872
+ total.update(wheres)
1873
+
1874
+ total.update(self._creates_related)
1875
+
1876
+ if not record:
1877
+ return self.create(total, id_key=self.get_primary_key())
1878
+ return record
1879
+
1880
+ def sole(self, query=False):
1881
+ """Gets the only record matching a given criteria."""
1882
+
1883
+ result = self.take(2).get()
1884
+
1885
+ if result.is_empty():
1886
+ raise ModelNotFound()
1887
+
1888
+ if result.count() > 1:
1889
+ raise MultipleRecordsFound()
1890
+
1891
+ return result.first()
1892
+
1893
+ def sole_value(self, column: str, query=False):
1894
+ return self.sole()[column]
1895
+
1896
+ def first_where(self, column, *args):
1897
+ """Gets the first record with the given key / value pair"""
1898
+ if not args:
1899
+ return self.where_not_null(column).first()
1900
+ return self.where(column, *args).first()
1901
+
1902
+ def last(self, column=None, query=False):
1903
+ """Gets the last record, ordered by column in descendant order or primary
1904
+ key if no column is given.
1905
+
1906
+ Returns:
1907
+ dictionary -- Returns a dictionary of results.
1908
+ """
1909
+ _column = column if column else self._model.get_primary_key()
1910
+ self.limit(1).order_by(_column, direction="DESC")
1911
+
1912
+ if query:
1913
+ return self
1914
+
1915
+ result = self.new_connection().query(
1916
+ self.to_qmark(),
1917
+ self._bindings,
1918
+ results=1,
1919
+ )
1920
+
1921
+ return self.prepare_result(result)
1922
+
1923
+ def _get_eager_load_result(self, related, collection):
1924
+ return related.eager_load_from_collection(collection)
1925
+
1926
+ def find(self, record_id, column=None, query=False):
1927
+ """Finds a row by the primary key ID. Requires a model
1928
+
1929
+ Arguments:
1930
+ record_id {int} -- The ID of the primary key to fetch.
1931
+
1932
+ Returns:
1933
+ Model|None
1934
+ """
1935
+ if not column:
1936
+ if not self._model:
1937
+ raise InvalidArgument("A colum to search is required")
1938
+
1939
+ column = self._model.get_primary_key()
1940
+
1941
+ if isinstance(record_id, (list, tuple)):
1942
+ self.where_in(column, record_id)
1943
+ else:
1944
+ self.where(column, record_id)
1945
+
1946
+ if query:
1947
+ return self
1948
+
1949
+ return self.first()
1950
+
1951
+ def find_or(
1952
+ self, record_id: int, callback: Callable, args=None, column=None
1953
+ ):
1954
+ """Finds a row by the primary key ID (Requires a model) or raise a ModelNotFound exception.
1955
+
1956
+ Arguments:
1957
+ record_id {int} -- The ID of the primary key to fetch.
1958
+ callback {Callable} -- The function to call if no record is found.
1959
+
1960
+ Returns:
1961
+ Model|Callable
1962
+ """
1963
+
1964
+ if not callable(callback):
1965
+ raise InvalidArgument("A callback must be callable.")
1966
+
1967
+ result = self.find(record_id=record_id, column=column)
1968
+
1969
+ if not result:
1970
+ if not args:
1971
+ return callback()
1972
+ else:
1973
+ return callback(*args)
1974
+
1975
+ return result
1976
+
1977
+ def find_or_fail(self, record_id, column=None):
1978
+ """Finds a row by the primary key ID (Requires a model) or raise a ModelNotFound exception.
1979
+
1980
+ Arguments:
1981
+ record_id {int} -- The ID of the primary key to fetch.
1982
+
1983
+ Returns:
1984
+ Model|ModelNotFound
1985
+ """
1986
+
1987
+ result = self.find(record_id=record_id, column=column)
1988
+
1989
+ if not result:
1990
+ raise ModelNotFound()
1991
+
1992
+ return result
1993
+
1994
+ def find_or_404(self, record_id, column=None):
1995
+ """Finds a row by the primary key ID (Requires a model) or raise an 404 exception.
1996
+
1997
+ Arguments:
1998
+ record_id {int} -- The ID of the primary key to fetch.
1999
+
2000
+ Returns:
2001
+ Model|HTTP404
2002
+ """
2003
+
2004
+ try:
2005
+ return self.find_or_fail(record_id=record_id, column=column)
2006
+ except ModelNotFound:
2007
+ raise HTTP404()
2008
+
2009
+ def first_or_fail(self, query=False):
2010
+ """Returns the first row from database. If no result found a ModelNotFound exception.
2011
+
2012
+ Returns:
2013
+ dictionary|ModelNotFound
2014
+ """
2015
+
2016
+ if query:
2017
+ return self.first(query=True)
2018
+
2019
+ result = self.first()
2020
+
2021
+ if not result:
2022
+ raise ModelNotFound()
2023
+
2024
+ return result
2025
+
2026
+ def get_primary_key(self):
2027
+ return self._model.get_primary_key()
2028
+
2029
+ def prepare_result(self, result, collection=False):
2030
+ if self._model and result:
2031
+ # eager load here
2032
+ hydrated_model = self._model.hydrate(result)
2033
+ if (
2034
+ self._eager_relation.eagers
2035
+ or self._eager_relation.nested_eagers
2036
+ or self._eager_relation.callback_eagers
2037
+ ) and hydrated_model:
2038
+ for eager_load in self._eager_relation.get_eagers():
2039
+ if isinstance(eager_load, dict):
2040
+ # Nested
2041
+ for relation, eagers in eager_load.items():
2042
+ callback = None
2043
+ if inspect.isclass(self._model):
2044
+ related = getattr(self._model, relation)
2045
+ elif callable(eagers):
2046
+ related = getattr(self._model, relation)
2047
+ callback = eagers
2048
+ else:
2049
+ related = self._model.get_related(relation)
2050
+
2051
+ result_set = related.get_related(
2052
+ self,
2053
+ hydrated_model,
2054
+ eagers=eagers,
2055
+ callback=callback,
2056
+ )
2057
+
2058
+ self._register_relationships_to_model(
2059
+ related,
2060
+ result_set,
2061
+ hydrated_model,
2062
+ relation_key=relation,
2063
+ )
2064
+ else:
2065
+ # Not Nested
2066
+ for eager in eager_load:
2067
+ if inspect.isclass(self._model):
2068
+ related = getattr(self._model, eager)
2069
+ else:
2070
+ related = self._model.get_related(eager)
2071
+
2072
+ result_set = related.get_related(
2073
+ self, hydrated_model
2074
+ )
2075
+
2076
+ self._register_relationships_to_model(
2077
+ related,
2078
+ result_set,
2079
+ hydrated_model,
2080
+ relation_key=eager,
2081
+ )
2082
+
2083
+ if collection:
2084
+ return hydrated_model if result else Collection([])
2085
+ else:
2086
+ return hydrated_model if result else None
2087
+
2088
+ if collection:
2089
+ return Collection(result) if result else Collection([])
2090
+ else:
2091
+ return result or None
2092
+
2093
+ def _register_relationships_to_model(
2094
+ self, related, related_result, hydrated_model, relation_key
2095
+ ):
2096
+ """Takes a related result and a hydrated model and registers them to eachother using the relation key.
2097
+
2098
+ Args:
2099
+ related_result (Model|Collection): Will be the related result based on the type of relationship.
2100
+ hydrated_model (Model|Collection): If a collection we will need to loop through the collection of models
2101
+ and register each one individually. Else we can just load the
2102
+ related_result into the hydrated_models
2103
+ relation_key (string): A key to bind the relationship with. Defaults to None.
2104
+
2105
+ Returns:
2106
+ self
2107
+ """
2108
+ if related_result and isinstance(hydrated_model, Collection):
2109
+ map_related = self._map_related(related_result, related)
2110
+ for model in hydrated_model:
2111
+ if isinstance(related_result, Collection):
2112
+ related.register_related(relation_key, model, map_related)
2113
+ else:
2114
+ model.add_relation({relation_key: map_related or None})
2115
+ else:
2116
+ hydrated_model.add_relation({relation_key: related_result or None})
2117
+ return self
2118
+
2119
+ def _map_related(self, related_result, related):
2120
+ return related.map_related(related_result)
2121
+
2122
+ def all(self, selects=[], query=False):
2123
+ """Returns all records from the table.
2124
+
2125
+ Returns:
2126
+ dictionary -- Returns a dictionary of results.
2127
+ """
2128
+
2129
+ self.select(*selects)
2130
+
2131
+ if query:
2132
+ return self
2133
+
2134
+ result = (
2135
+ self.new_connection().query(self.to_qmark(), self._bindings) or []
2136
+ )
2137
+
2138
+ return self.prepare_result(result, collection=True)
2139
+
2140
+ def get(self, selects=[]):
2141
+ """Runs the select query built from the query builder.
2142
+
2143
+ Returns:
2144
+ self
2145
+ """
2146
+ self.select(*selects)
2147
+ result = self.new_connection().query(self.to_qmark(), self._bindings)
2148
+
2149
+ return self.prepare_result(result, collection=True)
2150
+
2151
+ def new_connection(self):
2152
+ if self._connection:
2153
+ return self._connection
2154
+
2155
+ self._connection = (
2156
+ self.connection_class(
2157
+ **self.get_connection_information(), name=self.connection
2158
+ )
2159
+ .set_schema(self._schema)
2160
+ .make_connection()
2161
+ )
2162
+ return self._connection
2163
+
2164
+ def get_connection(self):
2165
+ return self._connection
2166
+
2167
+ def without_eager(self):
2168
+ self._should_eager = False
2169
+ return self
2170
+
2171
+ def with_(self, *eagers):
2172
+ self._eager_relation.register(eagers)
2173
+ return self
2174
+
2175
+ def paginate(self, per_page, page=1):
2176
+ if page == 1:
2177
+ offset = 0
2178
+ else:
2179
+ offset = (int(page) * per_page) - per_page
2180
+
2181
+ new_from_builder = self.new_from_builder()
2182
+ new_from_builder._order_by = ()
2183
+ new_from_builder._columns = ()
2184
+
2185
+ result = self.limit(per_page).offset(offset).get()
2186
+ total = new_from_builder.count()
2187
+
2188
+ paginator = LengthAwarePaginator(result, per_page, page, total)
2189
+ return paginator
2190
+
2191
+ def simple_paginate(self, per_page, page=1):
2192
+ if page == 1:
2193
+ offset = 0
2194
+ else:
2195
+ offset = (int(page) * per_page) - per_page
2196
+
2197
+ result = self.limit(per_page).offset(offset).get()
2198
+
2199
+ paginator = SimplePaginator(result, per_page, page)
2200
+ return paginator
2201
+
2202
+ def set_action(self, action):
2203
+ """Sets the action that the query builder should take when the query is built.
2204
+
2205
+ Arguments:
2206
+ action {string} -- The action that the query builder should take.
2207
+
2208
+ Returns:
2209
+ self
2210
+ """
2211
+ self._action = action
2212
+ return self
2213
+
2214
+ def get_grammar(self):
2215
+ """Initializes and returns the grammar class.
2216
+
2217
+ Returns:
2218
+ masoniteorm.grammar.Grammar -- An ORM grammar class.
2219
+ """
2220
+
2221
+ # Either _creates when creating, otherwise use columns
2222
+ columns = self._creates or self._columns
2223
+ if not columns and not self._aggregates and self._model:
2224
+ self.select(*self._model.get_selects())
2225
+ columns = self._columns
2226
+
2227
+ return self.grammar(
2228
+ columns=columns,
2229
+ table=self._table,
2230
+ wheres=self._wheres,
2231
+ limit=self._limit,
2232
+ offset=self._offset,
2233
+ updates=self._updates,
2234
+ aggregates=self._aggregates,
2235
+ order_by=self._order_by,
2236
+ group_by=self._group_by,
2237
+ distinct=self._distinct,
2238
+ lock=self.lock,
2239
+ joins=self._joins,
2240
+ having=self._having,
2241
+ )
2242
+
2243
+ def to_sql(self):
2244
+ """Compiles the QueryBuilder class into a SQL statement.
2245
+
2246
+ Returns:
2247
+ self
2248
+ """
2249
+
2250
+ self.run_scopes()
2251
+ grammar = self.get_grammar()
2252
+ sql = grammar.compile(self._action, qmark=False).to_sql()
2253
+ return sql
2254
+
2255
+ def explain(self):
2256
+ """Explains the Query execution plan.
2257
+
2258
+ Returns:
2259
+ Collection
2260
+ """
2261
+ sql = self.to_sql()
2262
+ explanation = self.statement(f"EXPLAIN {sql}")
2263
+ return explanation
2264
+
2265
+ def run_scopes(self):
2266
+ for name, scope in self._global_scopes.get(self._action, {}).items():
2267
+ scope(self)
2268
+
2269
+ return self
2270
+
2271
+ def to_qmark(self):
2272
+ """Compiles the QueryBuilder class into a Qmark SQL statement.
2273
+
2274
+ Returns:
2275
+ self
2276
+ """
2277
+
2278
+ self.run_scopes()
2279
+ grammar = self.get_grammar()
2280
+ sql = grammar.compile(self._action, qmark=True).to_sql()
2281
+
2282
+ self._bindings = grammar._bindings
2283
+
2284
+ self.reset()
2285
+
2286
+ return sql
2287
+
2288
+ def new(self):
2289
+ """Creates a new QueryBuilder class.
2290
+
2291
+ Returns:
2292
+ QueryBuilder -- The ORM QueryBuilder class.
2293
+ """
2294
+ builder = QueryBuilder(
2295
+ grammar=self.grammar,
2296
+ connection_class=self.connection_class,
2297
+ connection=self.connection,
2298
+ connection_driver=self._connection_driver,
2299
+ model=self._model,
2300
+ )
2301
+
2302
+ if self._table:
2303
+ builder.table(self._table.name)
2304
+
2305
+ return builder
2306
+
2307
+ def avg(self, column):
2308
+ """Aggregates a columns values.
2309
+
2310
+ Arguments:
2311
+ column {string} -- The name of the column to aggregate.
2312
+
2313
+ Returns:
2314
+ self
2315
+ """
2316
+ self.aggregate("AVG", "{column}".format(column=column))
2317
+ return self
2318
+
2319
+ def min(self, column):
2320
+ """Aggregates a columns values.
2321
+
2322
+ Arguments:
2323
+ column {string} -- The name of the column to aggregate.
2324
+
2325
+ Returns:
2326
+ self
2327
+ """
2328
+ self.aggregate("MIN", "{column}".format(column=column))
2329
+ return self
2330
+
2331
+ def _extract_operator_value(self, *args):
2332
+ operators = [
2333
+ "=",
2334
+ ">",
2335
+ ">=",
2336
+ "<",
2337
+ "<=",
2338
+ "!=",
2339
+ "<>",
2340
+ "like",
2341
+ "not like",
2342
+ "regexp",
2343
+ "not regexp",
2344
+ ]
2345
+
2346
+ operator = operators[0]
2347
+
2348
+ value = None
2349
+
2350
+ if (len(args)) >= 2:
2351
+ operator = args[0]
2352
+ value = args[1]
2353
+ elif len(args) == 1:
2354
+ value = args[0]
2355
+
2356
+ if operator not in operators:
2357
+ raise ValueError(
2358
+ "Invalid comparison operator. The operator can be %s"
2359
+ % ", ".join(operators)
2360
+ )
2361
+
2362
+ return operator, value
2363
+
2364
+ def __call__(self):
2365
+ """Magic method to standardize what happens when the query builder object is called.
2366
+
2367
+ Returns:
2368
+ self
2369
+ """
2370
+ return self
2371
+
2372
+ def macro(self, name, callable):
2373
+ self._macros.update({name: callable})
2374
+ return self
2375
+
2376
+ def when(self, conditional, callback):
2377
+ if conditional:
2378
+ callback(self)
2379
+ return self
2380
+
2381
+ def truncate(self, foreign_keys=False, dry=False):
2382
+ sql = self.get_grammar().truncate_table(
2383
+ self.get_table_name(), foreign_keys
2384
+ )
2385
+
2386
+ if dry or self.dry:
2387
+ return sql
2388
+
2389
+ return self.new_connection().query(sql, ())
2390
+
2391
+ def exists(self):
2392
+ """Determine if rows exist for the current query.
2393
+
2394
+ Returns:
2395
+ Bool - True or False
2396
+ """
2397
+ if self.first():
2398
+ return True
2399
+ else:
2400
+ return False
2401
+
2402
+ def doesnt_exist(self):
2403
+ """Determine if no rows exist for the current query.
2404
+
2405
+ Returns:
2406
+ Bool - True or False
2407
+ """
2408
+ if self.exists():
2409
+ return False
2410
+ else:
2411
+ return True
2412
+
2413
+ def in_random_order(self):
2414
+ """Puts Query results in random order"""
2415
+ return self.order_by_raw(self.grammar().compile_random())
2416
+
2417
+ def new_from_builder(self, from_builder=None):
2418
+ """Creates a new QueryBuilder class.
2419
+
2420
+ Returns:
2421
+ QueryBuilder -- The ORM QueryBuilder class.
2422
+ """
2423
+ if from_builder is None:
2424
+ from_builder = self
2425
+
2426
+ builder = QueryBuilder(
2427
+ grammar=self.grammar,
2428
+ connection_class=self.connection_class,
2429
+ connection=self.connection,
2430
+ connection_driver=self._connection_driver,
2431
+ )
2432
+
2433
+ if self._table:
2434
+ builder.table(self._table.name)
2435
+
2436
+ builder._columns = deepcopy(from_builder._columns)
2437
+ builder._creates = deepcopy(from_builder._creates)
2438
+ builder._sql = ""
2439
+ builder._bindings = deepcopy(from_builder._bindings)
2440
+ builder._updates = deepcopy(from_builder._updates)
2441
+ builder._wheres = deepcopy(from_builder._wheres)
2442
+ builder._order_by = deepcopy(from_builder._order_by)
2443
+ builder._group_by = deepcopy(from_builder._group_by)
2444
+ builder._joins = deepcopy(from_builder._joins)
2445
+ builder._having = deepcopy(from_builder._having)
2446
+ builder._macros = deepcopy(from_builder._macros)
2447
+ builder._aggregates = deepcopy(from_builder._aggregates)
2448
+ builder._global_scopes = deepcopy(from_builder._global_scopes)
2449
+
2450
+ return builder
2451
+
2452
+ def get_table_columns(self):
2453
+ return self.get_schema().get_columns(self._table.name)
2454
+
2455
+ def get_schema(self):
2456
+ return Schema(
2457
+ connection=self.connection,
2458
+ connection_details=self._connection_details,
2459
+ )
2460
+
2461
+ def latest(self, *fields):
2462
+ """Gets the latest record.
2463
+
2464
+ Returns:
2465
+ querybuilder
2466
+ """
2467
+
2468
+ if not fields:
2469
+ fields = ("created_at",)
2470
+
2471
+ return self.order_by(column=",".join(fields), direction="DESC")
2472
+
2473
+ def oldest(self, *fields):
2474
+ """Gets the oldest record.
2475
+
2476
+ Returns:
2477
+ querybuilder
2478
+ """
2479
+
2480
+ if not fields:
2481
+ fields = ("created_at",)
2482
+
2483
+ return self.order_by(column=",".join(fields), direction="ASC")
2484
+
2485
+ def value(self, column: str):
2486
+ return self.get().first()[column]