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.
- masonite_framework_orm-3.0.1.dist-info/METADATA +87 -0
- masonite_framework_orm-3.0.1.dist-info/RECORD +116 -0
- masonite_framework_orm-3.0.1.dist-info/WHEEL +5 -0
- masonite_framework_orm-3.0.1.dist-info/entry_points.txt +3 -0
- masonite_framework_orm-3.0.1.dist-info/licenses/LICENSE +21 -0
- masonite_framework_orm-3.0.1.dist-info/top_level.txt +1 -0
- masoniteorm/__init__.py +1 -0
- masoniteorm/collection/Collection.py +605 -0
- masoniteorm/collection/__init__.py +1 -0
- masoniteorm/commands/CanOverrideConfig.py +16 -0
- masoniteorm/commands/CanOverrideOptionsDefault.py +22 -0
- masoniteorm/commands/Command.py +6 -0
- masoniteorm/commands/Entry.py +43 -0
- masoniteorm/commands/MakeMigrationCommand.py +57 -0
- masoniteorm/commands/MakeModelCommand.py +78 -0
- masoniteorm/commands/MakeModelDocstringCommand.py +37 -0
- masoniteorm/commands/MakeObserverCommand.py +54 -0
- masoniteorm/commands/MakeSeedCommand.py +54 -0
- masoniteorm/commands/MigrateCommand.py +46 -0
- masoniteorm/commands/MigrateFreshCommand.py +41 -0
- masoniteorm/commands/MigrateRefreshCommand.py +41 -0
- masoniteorm/commands/MigrateResetCommand.py +25 -0
- masoniteorm/commands/MigrateRollbackCommand.py +26 -0
- masoniteorm/commands/MigrateStatusCommand.py +51 -0
- masoniteorm/commands/SeedRunCommand.py +35 -0
- masoniteorm/commands/ShellCommand.py +205 -0
- masoniteorm/commands/__init__.py +18 -0
- masoniteorm/commands/stubs/create_migration.stub +20 -0
- masoniteorm/commands/stubs/create_seed.stub +9 -0
- masoniteorm/commands/stubs/model.stub +9 -0
- masoniteorm/commands/stubs/observer.stub +101 -0
- masoniteorm/commands/stubs/table_migration.stub +19 -0
- masoniteorm/config.py +123 -0
- masoniteorm/connections/BaseConnection.py +101 -0
- masoniteorm/connections/ConnectionFactory.py +59 -0
- masoniteorm/connections/ConnectionResolver.py +132 -0
- masoniteorm/connections/MSSQLConnection.py +176 -0
- masoniteorm/connections/MySQLConnection.py +232 -0
- masoniteorm/connections/PostgresConnection.py +225 -0
- masoniteorm/connections/SQLiteConnection.py +179 -0
- masoniteorm/connections/__init__.py +6 -0
- masoniteorm/exceptions.py +38 -0
- masoniteorm/expressions/__init__.py +1 -0
- masoniteorm/expressions/expressions.py +288 -0
- masoniteorm/factories/Factory.py +112 -0
- masoniteorm/factories/__init__.py +1 -0
- masoniteorm/helpers/__init__.py +0 -0
- masoniteorm/helpers/misc.py +22 -0
- masoniteorm/migrations/Migration.py +330 -0
- masoniteorm/migrations/__init__.py +1 -0
- masoniteorm/models/MigrationModel.py +9 -0
- masoniteorm/models/Model.py +1209 -0
- masoniteorm/models/Model.pyi +1366 -0
- masoniteorm/models/Pivot.py +5 -0
- masoniteorm/models/__init__.py +1 -0
- masoniteorm/observers/ObservesEvents.py +27 -0
- masoniteorm/observers/__init__.py +1 -0
- masoniteorm/pagination/BasePaginator.py +10 -0
- masoniteorm/pagination/LengthAwarePaginator.py +34 -0
- masoniteorm/pagination/SimplePaginator.py +28 -0
- masoniteorm/pagination/__init__.py +2 -0
- masoniteorm/providers/ORMProvider.py +39 -0
- masoniteorm/providers/__init__.py +1 -0
- masoniteorm/query/EagerRelation.py +42 -0
- masoniteorm/query/QueryBuilder.py +2486 -0
- masoniteorm/query/__init__.py +1 -0
- masoniteorm/query/grammars/BaseGrammar.py +1027 -0
- masoniteorm/query/grammars/MSSQLGrammar.py +194 -0
- masoniteorm/query/grammars/MySQLGrammar.py +238 -0
- masoniteorm/query/grammars/PostgresGrammar.py +213 -0
- masoniteorm/query/grammars/SQLiteGrammar.py +228 -0
- masoniteorm/query/grammars/__init__.py +4 -0
- masoniteorm/query/processors/MSSQLPostProcessor.py +58 -0
- masoniteorm/query/processors/MySQLPostProcessor.py +48 -0
- masoniteorm/query/processors/PostgresPostProcessor.py +49 -0
- masoniteorm/query/processors/SQLitePostProcessor.py +49 -0
- masoniteorm/query/processors/__init__.py +4 -0
- masoniteorm/relationships/BaseRelationship.py +161 -0
- masoniteorm/relationships/BelongsTo.py +124 -0
- masoniteorm/relationships/BelongsToMany.py +604 -0
- masoniteorm/relationships/HasMany.py +66 -0
- masoniteorm/relationships/HasManyThrough.py +269 -0
- masoniteorm/relationships/HasOne.py +111 -0
- masoniteorm/relationships/HasOneThrough.py +275 -0
- masoniteorm/relationships/MorphMany.py +152 -0
- masoniteorm/relationships/MorphOne.py +156 -0
- masoniteorm/relationships/MorphTo.py +111 -0
- masoniteorm/relationships/MorphToMany.py +108 -0
- masoniteorm/relationships/__init__.py +10 -0
- masoniteorm/schema/Blueprint.py +1161 -0
- masoniteorm/schema/Column.py +144 -0
- masoniteorm/schema/ColumnDiff.py +0 -0
- masoniteorm/schema/Constraint.py +5 -0
- masoniteorm/schema/ForeignKeyConstraint.py +28 -0
- masoniteorm/schema/Index.py +5 -0
- masoniteorm/schema/Schema.py +359 -0
- masoniteorm/schema/Table.py +94 -0
- masoniteorm/schema/TableDiff.py +86 -0
- masoniteorm/schema/__init__.py +3 -0
- masoniteorm/schema/platforms/MSSQLPlatform.py +367 -0
- masoniteorm/schema/platforms/MySQLPlatform.py +513 -0
- masoniteorm/schema/platforms/Platform.py +97 -0
- masoniteorm/schema/platforms/PostgresPlatform.py +551 -0
- masoniteorm/schema/platforms/SQLitePlatform.py +481 -0
- masoniteorm/schema/platforms/__init__.py +4 -0
- masoniteorm/scopes/BaseScope.py +6 -0
- masoniteorm/scopes/SoftDeleteScope.py +56 -0
- masoniteorm/scopes/SoftDeletesMixin.py +13 -0
- masoniteorm/scopes/TimeStampsMixin.py +12 -0
- masoniteorm/scopes/TimeStampsScope.py +47 -0
- masoniteorm/scopes/UUIDPrimaryKeyMixin.py +8 -0
- masoniteorm/scopes/UUIDPrimaryKeyScope.py +51 -0
- masoniteorm/scopes/__init__.py +8 -0
- masoniteorm/scopes/scope.py +15 -0
- masoniteorm/seeds/Seeder.py +42 -0
- 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]
|