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,481 @@
|
|
|
1
|
+
from ...schema import Schema
|
|
2
|
+
from ..Table import Table
|
|
3
|
+
from .Platform import Platform
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SQLitePlatform(Platform):
|
|
7
|
+
types_without_lengths = [
|
|
8
|
+
"integer",
|
|
9
|
+
"big_integer",
|
|
10
|
+
"tiny_integer",
|
|
11
|
+
"small_integer",
|
|
12
|
+
"medium_integer",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
types_without_signs = ["decimal"]
|
|
16
|
+
|
|
17
|
+
type_map = {
|
|
18
|
+
"string": "VARCHAR",
|
|
19
|
+
"char": "CHAR",
|
|
20
|
+
"integer": "INTEGER",
|
|
21
|
+
"big_integer": "BIGINT",
|
|
22
|
+
"tiny_integer": "TINYINT",
|
|
23
|
+
"big_increments": "BIGINT",
|
|
24
|
+
"small_integer": "SMALLINT",
|
|
25
|
+
"medium_integer": "MEDIUMINT",
|
|
26
|
+
"integer_unsigned": "INT UNSIGNED",
|
|
27
|
+
"big_integer_unsigned": "BIGINT UNSIGNED",
|
|
28
|
+
"tiny_integer_unsigned": "TINYINT UNSIGNED",
|
|
29
|
+
"small_integer_unsigned": "SMALLINT UNSIGNED",
|
|
30
|
+
"medium_integer_unsigned": "MEDIUMINT UNSIGNED",
|
|
31
|
+
"increments": "INTEGER",
|
|
32
|
+
"uuid": "CHAR",
|
|
33
|
+
"binary": "LONGBLOB",
|
|
34
|
+
"boolean": "BOOLEAN",
|
|
35
|
+
"decimal": "DECIMAL",
|
|
36
|
+
"double": "DOUBLE",
|
|
37
|
+
"enum": "VARCHAR",
|
|
38
|
+
"text": "TEXT",
|
|
39
|
+
"tiny_text": "TEXT",
|
|
40
|
+
"float": "FLOAT",
|
|
41
|
+
"geometry": "GEOMETRY",
|
|
42
|
+
"json": "JSON",
|
|
43
|
+
"jsonb": "LONGBLOB",
|
|
44
|
+
"inet": "VARCHAR",
|
|
45
|
+
"cidr": "VARCHAR",
|
|
46
|
+
"macaddr": "VARCHAR",
|
|
47
|
+
"long_text": "LONGTEXT",
|
|
48
|
+
"point": "POINT",
|
|
49
|
+
"time": "TIME",
|
|
50
|
+
"timestamp": "TIMESTAMP",
|
|
51
|
+
"date": "DATE",
|
|
52
|
+
"year": "VARCHAR",
|
|
53
|
+
"datetime": "DATETIME",
|
|
54
|
+
"tiny_increments": "TINYINT AUTO_INCREMENT",
|
|
55
|
+
"unsigned": "INT UNSIGNED",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
premapped_defaults = {
|
|
59
|
+
"current": " DEFAULT CURRENT_TIMESTAMP",
|
|
60
|
+
"now": " DEFAULT NOW()",
|
|
61
|
+
"null": " DEFAULT NULL",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
premapped_nulls = {True: "NULL", False: "NOT NULL"}
|
|
65
|
+
|
|
66
|
+
def compile_create_sql(self, table, if_not_exists=False):
|
|
67
|
+
sql = []
|
|
68
|
+
table_create_format = (
|
|
69
|
+
self.create_if_not_exists_format()
|
|
70
|
+
if if_not_exists
|
|
71
|
+
else self.create_format()
|
|
72
|
+
)
|
|
73
|
+
sql.append(
|
|
74
|
+
table_create_format.format(
|
|
75
|
+
table=self.get_table_string().format(table=table.name).strip(),
|
|
76
|
+
columns=", ".join(
|
|
77
|
+
self.columnize(table.get_added_columns())
|
|
78
|
+
).strip(),
|
|
79
|
+
constraints=(
|
|
80
|
+
", "
|
|
81
|
+
+ ", ".join(
|
|
82
|
+
self.constraintize(table.get_added_constraints())
|
|
83
|
+
)
|
|
84
|
+
if table.get_added_constraints()
|
|
85
|
+
else ""
|
|
86
|
+
),
|
|
87
|
+
foreign_keys=(
|
|
88
|
+
", "
|
|
89
|
+
+ ", ".join(
|
|
90
|
+
self.foreign_key_constraintize(
|
|
91
|
+
table.name, table.added_foreign_keys
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
if table.added_foreign_keys
|
|
95
|
+
else ""
|
|
96
|
+
),
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if table.added_indexes:
|
|
101
|
+
for name, index in table.added_indexes.items():
|
|
102
|
+
sql.append(
|
|
103
|
+
f"CREATE INDEX {index.name} ON {self.wrap_table(table.name)}({','.join(index.column)})"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
return sql
|
|
107
|
+
|
|
108
|
+
def columnize(self, columns):
|
|
109
|
+
sql = []
|
|
110
|
+
for name, column in columns.items():
|
|
111
|
+
if column.length:
|
|
112
|
+
length = self.create_column_length(column.column_type).format(
|
|
113
|
+
length=column.length
|
|
114
|
+
)
|
|
115
|
+
else:
|
|
116
|
+
length = ""
|
|
117
|
+
|
|
118
|
+
if column.default == "":
|
|
119
|
+
default = " DEFAULT ''"
|
|
120
|
+
elif column.default in (0,):
|
|
121
|
+
default = f" DEFAULT {column.default}"
|
|
122
|
+
elif column.default in self.premapped_defaults.keys():
|
|
123
|
+
default = self.premapped_defaults.get(column.default)
|
|
124
|
+
elif column.default:
|
|
125
|
+
if (
|
|
126
|
+
isinstance(column.default, (str,))
|
|
127
|
+
and not column.default_is_raw
|
|
128
|
+
):
|
|
129
|
+
default = f" DEFAULT '{column.default}'"
|
|
130
|
+
else:
|
|
131
|
+
default = f" DEFAULT {column.default}"
|
|
132
|
+
else:
|
|
133
|
+
default = ""
|
|
134
|
+
|
|
135
|
+
constraint = ""
|
|
136
|
+
column_constraint = ""
|
|
137
|
+
if column.primary:
|
|
138
|
+
constraint = "PRIMARY KEY"
|
|
139
|
+
|
|
140
|
+
if column.column_type == "enum":
|
|
141
|
+
values = ", ".join(f"'{x}'" for x in column.values)
|
|
142
|
+
column_constraint = f" CHECK({column.name} IN ({values}))"
|
|
143
|
+
|
|
144
|
+
sql.append(
|
|
145
|
+
self.columnize_string()
|
|
146
|
+
.format(
|
|
147
|
+
name=self.wrap_column(column.name),
|
|
148
|
+
data_type=self.type_map.get(column.column_type, ""),
|
|
149
|
+
column_constraint=column_constraint,
|
|
150
|
+
length=length,
|
|
151
|
+
signed=(
|
|
152
|
+
" " + self.signed.get(column._signed)
|
|
153
|
+
if column.column_type not in self.types_without_signs
|
|
154
|
+
and column._signed
|
|
155
|
+
else ""
|
|
156
|
+
),
|
|
157
|
+
constraint=constraint,
|
|
158
|
+
nullable=self.premapped_nulls.get(column.is_null) or "",
|
|
159
|
+
default=default,
|
|
160
|
+
)
|
|
161
|
+
.strip()
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
return sql
|
|
165
|
+
|
|
166
|
+
def compile_alter_sql(self, diff):
|
|
167
|
+
sql = []
|
|
168
|
+
if diff.removed_indexes or diff.removed_unique_indexes:
|
|
169
|
+
indexes = diff.removed_indexes
|
|
170
|
+
indexes += diff.removed_unique_indexes
|
|
171
|
+
for name in indexes:
|
|
172
|
+
sql.append("DROP INDEX {name}".format(name=name))
|
|
173
|
+
|
|
174
|
+
if diff.added_columns:
|
|
175
|
+
for name, column in diff.added_columns.items():
|
|
176
|
+
default = ""
|
|
177
|
+
if column.default in (0,):
|
|
178
|
+
default = f" DEFAULT {column.default}"
|
|
179
|
+
elif column.default in self.premapped_defaults.keys():
|
|
180
|
+
default = self.premapped_defaults.get(column.default)
|
|
181
|
+
elif column.default:
|
|
182
|
+
if isinstance(column.default, (str,)):
|
|
183
|
+
default = f" DEFAULT '{column.default}'"
|
|
184
|
+
else:
|
|
185
|
+
default = f" DEFAULT {column.default}"
|
|
186
|
+
else:
|
|
187
|
+
default = ""
|
|
188
|
+
constraint = ""
|
|
189
|
+
column_constraint = ""
|
|
190
|
+
if column.name in diff.added_foreign_keys:
|
|
191
|
+
foreign_key = diff.added_foreign_keys[column.name]
|
|
192
|
+
constraint = f" REFERENCES {self.wrap_table(foreign_key.foreign_table)}({self.wrap_column(foreign_key.foreign_column)})"
|
|
193
|
+
if column.column_type == "enum":
|
|
194
|
+
values = ", ".join(f"'{x}'" for x in column.values)
|
|
195
|
+
column_constraint = f" CHECK('{column.name}' IN({values}))"
|
|
196
|
+
|
|
197
|
+
sql.append(
|
|
198
|
+
self.add_column_string()
|
|
199
|
+
.format(
|
|
200
|
+
table=self.wrap_table(diff.name),
|
|
201
|
+
name=self.wrap_column(column.name),
|
|
202
|
+
data_type=self.type_map.get(column.column_type, ""),
|
|
203
|
+
column_constraint=column_constraint,
|
|
204
|
+
nullable="NULL" if column.is_null else "NOT NULL",
|
|
205
|
+
default=default,
|
|
206
|
+
signed=(
|
|
207
|
+
" " + self.signed.get(column._signed)
|
|
208
|
+
if column.column_type
|
|
209
|
+
not in self.types_without_signs
|
|
210
|
+
and column._signed
|
|
211
|
+
else ""
|
|
212
|
+
),
|
|
213
|
+
constraint=constraint,
|
|
214
|
+
)
|
|
215
|
+
.strip()
|
|
216
|
+
)
|
|
217
|
+
if (
|
|
218
|
+
diff.renamed_columns
|
|
219
|
+
or diff.dropped_columns
|
|
220
|
+
or diff.changed_columns
|
|
221
|
+
or diff.added_foreign_keys
|
|
222
|
+
):
|
|
223
|
+
original_columns = diff.from_table.added_columns
|
|
224
|
+
# pop off the dropped columns. No need for them here
|
|
225
|
+
for column in diff.dropped_columns:
|
|
226
|
+
original_columns.pop(column)
|
|
227
|
+
|
|
228
|
+
sql.append(
|
|
229
|
+
"CREATE TEMPORARY TABLE __temp__{table} AS SELECT {original_column_names} FROM {table}".format(
|
|
230
|
+
table=diff.name,
|
|
231
|
+
original_column_names=", ".join(
|
|
232
|
+
diff.from_table.added_columns.keys()
|
|
233
|
+
),
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
sql.append(
|
|
238
|
+
"DROP TABLE {table}".format(table=self.wrap_table(diff.name))
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
columns = diff.from_table.added_columns
|
|
242
|
+
|
|
243
|
+
columns.update(diff.renamed_columns)
|
|
244
|
+
columns.update(diff.changed_columns)
|
|
245
|
+
columns.update(diff.added_columns)
|
|
246
|
+
|
|
247
|
+
sql.append(
|
|
248
|
+
self.create_format().format(
|
|
249
|
+
table=self.get_table_string()
|
|
250
|
+
.format(table=diff.name)
|
|
251
|
+
.strip(),
|
|
252
|
+
columns=", ".join(self.columnize(columns)).strip(),
|
|
253
|
+
constraints=(
|
|
254
|
+
", "
|
|
255
|
+
+ ", ".join(
|
|
256
|
+
self.constraintize(diff.get_added_constraints())
|
|
257
|
+
)
|
|
258
|
+
if diff.get_added_constraints()
|
|
259
|
+
else ""
|
|
260
|
+
),
|
|
261
|
+
foreign_keys=(
|
|
262
|
+
", "
|
|
263
|
+
+ ", ".join(
|
|
264
|
+
self.foreign_key_constraintize(
|
|
265
|
+
diff.name, diff.added_foreign_keys
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
if diff.added_foreign_keys
|
|
269
|
+
else ""
|
|
270
|
+
),
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
for column in diff.added_columns:
|
|
275
|
+
columns.pop(column)
|
|
276
|
+
|
|
277
|
+
sql.append(
|
|
278
|
+
"INSERT INTO {quoted_table} ({new_columns}) SELECT {original_column_names} FROM __temp__{table}".format(
|
|
279
|
+
quoted_table=self.wrap_table(diff.name),
|
|
280
|
+
table=diff.name,
|
|
281
|
+
new_columns=", ".join(self.columnize_names(columns)),
|
|
282
|
+
original_column_names=", ".join(
|
|
283
|
+
diff.from_table.added_columns.keys()
|
|
284
|
+
),
|
|
285
|
+
)
|
|
286
|
+
)
|
|
287
|
+
sql.append("DROP TABLE __temp__{table}".format(table=diff.name))
|
|
288
|
+
|
|
289
|
+
if diff.new_name:
|
|
290
|
+
sql.append(
|
|
291
|
+
"ALTER TABLE {old_name} RENAME TO {new_name}".format(
|
|
292
|
+
old_name=self.wrap_table(diff.name),
|
|
293
|
+
new_name=self.wrap_table(diff.new_name),
|
|
294
|
+
)
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
if diff.added_indexes:
|
|
298
|
+
for name, index in diff.added_indexes.items():
|
|
299
|
+
sql.append(
|
|
300
|
+
f"CREATE INDEX {index.name} ON {self.wrap_table(diff.name)}({','.join(index.column)})"
|
|
301
|
+
)
|
|
302
|
+
if diff.added_constraints:
|
|
303
|
+
for name, constraint in diff.added_constraints.items():
|
|
304
|
+
if constraint.constraint_type == "unique":
|
|
305
|
+
sql.append(
|
|
306
|
+
f"CREATE UNIQUE INDEX {constraint.name} ON {self.wrap_table(diff.name)}({','.join(constraint.columns if isinstance(constraint.columns, list) else [constraint.columns])})"
|
|
307
|
+
)
|
|
308
|
+
elif constraint.constraint_type == "primary_key":
|
|
309
|
+
sql.append(
|
|
310
|
+
f"ALTER TABLE {self.wrap_table(diff.name)} ADD CONSTRAINT {constraint.name} PRIMARY KEY ({','.join(constraint.columns)})"
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
return sql
|
|
314
|
+
|
|
315
|
+
def create_format(self):
|
|
316
|
+
return "CREATE TABLE {table} ({columns}{constraints}{foreign_keys})"
|
|
317
|
+
|
|
318
|
+
def create_if_not_exists_format(self):
|
|
319
|
+
return "CREATE TABLE IF NOT EXISTS {table} ({columns}{constraints}{foreign_keys})"
|
|
320
|
+
|
|
321
|
+
def get_table_string(self):
|
|
322
|
+
return '"{table}"'
|
|
323
|
+
|
|
324
|
+
def get_column_string(self):
|
|
325
|
+
return '"{column}"'
|
|
326
|
+
|
|
327
|
+
def add_column_string(self):
|
|
328
|
+
return "ALTER TABLE {table} ADD COLUMN {name} {data_type}{column_constraint}{signed} {nullable}{default}{constraint}"
|
|
329
|
+
|
|
330
|
+
def create_column_length(self, column_type):
|
|
331
|
+
if column_type in self.types_without_lengths:
|
|
332
|
+
return ""
|
|
333
|
+
return "({length})"
|
|
334
|
+
|
|
335
|
+
def columnize_string(self):
|
|
336
|
+
return "{name} {data_type}{length}{column_constraint}{signed} {nullable}{default} {constraint}"
|
|
337
|
+
|
|
338
|
+
def get_unique_constraint_string(self):
|
|
339
|
+
return "UNIQUE({columns})"
|
|
340
|
+
|
|
341
|
+
def get_foreign_key_constraint_string(self):
|
|
342
|
+
return "CONSTRAINT {constraint_name} FOREIGN KEY ({column}) REFERENCES {foreign_table}({foreign_column}){cascade}"
|
|
343
|
+
|
|
344
|
+
def get_primary_key_constraint_string(self):
|
|
345
|
+
return "CONSTRAINT {constraint_name} PRIMARY KEY ({columns})"
|
|
346
|
+
|
|
347
|
+
def constraintize(self, constraints):
|
|
348
|
+
sql = []
|
|
349
|
+
for name, constraint in constraints.items():
|
|
350
|
+
sql.append(
|
|
351
|
+
getattr(
|
|
352
|
+
self, f"get_{constraint.constraint_type}_constraint_string"
|
|
353
|
+
)().format(
|
|
354
|
+
columns=", ".join(constraint.columns),
|
|
355
|
+
constraint_name=constraint.name,
|
|
356
|
+
)
|
|
357
|
+
)
|
|
358
|
+
return sql
|
|
359
|
+
|
|
360
|
+
def foreign_key_constraintize(self, table, foreign_keys):
|
|
361
|
+
sql = []
|
|
362
|
+
for name, foreign_key in foreign_keys.items():
|
|
363
|
+
cascade = ""
|
|
364
|
+
if foreign_key.delete_action:
|
|
365
|
+
cascade += f" ON DELETE {self.foreign_key_actions.get(foreign_key.delete_action.lower())}"
|
|
366
|
+
if foreign_key.update_action:
|
|
367
|
+
cascade += f" ON UPDATE {self.foreign_key_actions.get(foreign_key.update_action.lower())}"
|
|
368
|
+
sql.append(
|
|
369
|
+
self.get_foreign_key_constraint_string().format(
|
|
370
|
+
column=self.wrap_column(foreign_key.column),
|
|
371
|
+
constraint_name=foreign_key.constraint_name,
|
|
372
|
+
table=self.wrap_table(table),
|
|
373
|
+
foreign_table=self.wrap_table(foreign_key.foreign_table),
|
|
374
|
+
foreign_column=self.wrap_column(
|
|
375
|
+
foreign_key.foreign_column
|
|
376
|
+
),
|
|
377
|
+
cascade=cascade,
|
|
378
|
+
)
|
|
379
|
+
)
|
|
380
|
+
return sql
|
|
381
|
+
|
|
382
|
+
def columnize_names(self, columns):
|
|
383
|
+
names = []
|
|
384
|
+
for name, column in columns.items():
|
|
385
|
+
names.append(self.wrap_column(column.name))
|
|
386
|
+
|
|
387
|
+
return names
|
|
388
|
+
|
|
389
|
+
def get_current_schema(self, connection, table_name, schema=None):
|
|
390
|
+
sql = f"PRAGMA table_info({table_name})"
|
|
391
|
+
|
|
392
|
+
reversed_type_map = {v: k for k, v in self.type_map.items()}
|
|
393
|
+
table = Table(table_name)
|
|
394
|
+
|
|
395
|
+
result = connection.query(sql, ())
|
|
396
|
+
for column in result:
|
|
397
|
+
column_type = self.get_column_type(
|
|
398
|
+
reversed_type_map, column["type"].upper()
|
|
399
|
+
)
|
|
400
|
+
length = self.get_column_length(column["type"])
|
|
401
|
+
|
|
402
|
+
# find default
|
|
403
|
+
default = column.get("dflt_value")
|
|
404
|
+
if default:
|
|
405
|
+
default = default.replace("'", "")
|
|
406
|
+
|
|
407
|
+
table.add_column(
|
|
408
|
+
column["name"],
|
|
409
|
+
column_type,
|
|
410
|
+
column_python_type=Schema._type_hints_map.get(
|
|
411
|
+
column_type, str
|
|
412
|
+
),
|
|
413
|
+
default=default,
|
|
414
|
+
length=length,
|
|
415
|
+
nullable=int(column.get("notnull")) == 0,
|
|
416
|
+
)
|
|
417
|
+
if column.get("pk") == 1:
|
|
418
|
+
table.set_primary_key(column["name"])
|
|
419
|
+
|
|
420
|
+
return table
|
|
421
|
+
|
|
422
|
+
def get_column_length(self, column_type):
|
|
423
|
+
if "(" in column_type:
|
|
424
|
+
parenthesis_index = column_type.find("(")
|
|
425
|
+
return column_type[parenthesis_index + 1 : -1]
|
|
426
|
+
else:
|
|
427
|
+
return None
|
|
428
|
+
|
|
429
|
+
def get_column_type(self, reversed_type_map, column_type):
|
|
430
|
+
if "(" in column_type:
|
|
431
|
+
parenthesis_index = column_type.find("(")
|
|
432
|
+
db_type = column_type[:parenthesis_index]
|
|
433
|
+
length = self.get_column_length(column_type)
|
|
434
|
+
if db_type == "CHAR":
|
|
435
|
+
if length == "1":
|
|
436
|
+
return "char"
|
|
437
|
+
elif length == "36":
|
|
438
|
+
return "uuid"
|
|
439
|
+
else:
|
|
440
|
+
return "char"
|
|
441
|
+
elif db_type == "VARCHAR":
|
|
442
|
+
if length == "4":
|
|
443
|
+
return "year"
|
|
444
|
+
else:
|
|
445
|
+
return "string"
|
|
446
|
+
else:
|
|
447
|
+
return reversed_type_map.get(column_type)
|
|
448
|
+
|
|
449
|
+
def compile_table_exists(self, table, database=None, schema=None):
|
|
450
|
+
return f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table}'"
|
|
451
|
+
|
|
452
|
+
def compile_column_exists(self, table, column):
|
|
453
|
+
return f"SELECT column_name FROM information_schema.columns WHERE table_name='{table}' and column_name='{column}'"
|
|
454
|
+
|
|
455
|
+
def compile_get_all_tables(self, database, schema=None):
|
|
456
|
+
return "SELECT name FROM sqlite_master WHERE type='table'"
|
|
457
|
+
|
|
458
|
+
def compile_truncate(self, table, foreign_keys=False):
|
|
459
|
+
if not foreign_keys:
|
|
460
|
+
return f"DELETE FROM {self.wrap_table(table)}"
|
|
461
|
+
|
|
462
|
+
return [
|
|
463
|
+
self.disable_foreign_key_constraints(),
|
|
464
|
+
f"DELETE FROM {self.wrap_table(table)}",
|
|
465
|
+
self.enable_foreign_key_constraints(),
|
|
466
|
+
]
|
|
467
|
+
|
|
468
|
+
def compile_rename_table(self, current_table, new_name):
|
|
469
|
+
return f"ALTER TABLE {self.wrap_table(current_table)} RENAME TO {self.wrap_table(new_name)}"
|
|
470
|
+
|
|
471
|
+
def compile_drop_table_if_exists(self, current_table):
|
|
472
|
+
return f"DROP TABLE IF EXISTS {self.wrap_table(current_table)}"
|
|
473
|
+
|
|
474
|
+
def compile_drop_table(self, current_table):
|
|
475
|
+
return f"DROP TABLE {self.wrap_table(current_table)}"
|
|
476
|
+
|
|
477
|
+
def enable_foreign_key_constraints(self):
|
|
478
|
+
return "PRAGMA foreign_keys = ON"
|
|
479
|
+
|
|
480
|
+
def disable_foreign_key_constraints(self):
|
|
481
|
+
return "PRAGMA foreign_keys = OFF"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from .BaseScope import BaseScope
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SoftDeleteScope(BaseScope):
|
|
5
|
+
"""Global scope class to add soft deleting to models."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, deleted_at_column="deleted_at"):
|
|
8
|
+
self.deleted_at_column = deleted_at_column
|
|
9
|
+
|
|
10
|
+
def on_boot(self, builder):
|
|
11
|
+
builder.set_global_scope(
|
|
12
|
+
"_where_null", self._where_null, action="select"
|
|
13
|
+
)
|
|
14
|
+
builder.set_global_scope(
|
|
15
|
+
"_query_set_null_on_delete",
|
|
16
|
+
self._query_set_null_on_delete,
|
|
17
|
+
action="delete",
|
|
18
|
+
)
|
|
19
|
+
builder.macro("with_trashed", self._with_trashed)
|
|
20
|
+
builder.macro("only_trashed", self._only_trashed)
|
|
21
|
+
builder.macro("force_delete", self._force_delete)
|
|
22
|
+
builder.macro("restore", self._restore)
|
|
23
|
+
|
|
24
|
+
def on_remove(self, builder):
|
|
25
|
+
builder.remove_global_scope("_where_null", action="select")
|
|
26
|
+
builder.remove_global_scope(
|
|
27
|
+
"_query_set_null_on_delete", action="delete"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def _where_null(self, builder):
|
|
31
|
+
return builder.where_null(
|
|
32
|
+
f"{builder.get_table_name()}.{self.deleted_at_column}"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def _with_trashed(self, model, builder):
|
|
36
|
+
builder.remove_global_scope("_where_null", action="select")
|
|
37
|
+
return builder
|
|
38
|
+
|
|
39
|
+
def _only_trashed(self, model, builder):
|
|
40
|
+
builder.remove_global_scope("_where_null", action="select")
|
|
41
|
+
return builder.where_not_null(self.deleted_at_column)
|
|
42
|
+
|
|
43
|
+
def _force_delete(self, model, builder, query=False):
|
|
44
|
+
if query:
|
|
45
|
+
return builder.remove_global_scope(self).set_action("delete")
|
|
46
|
+
return builder.remove_global_scope(self).delete()
|
|
47
|
+
|
|
48
|
+
def _restore(self, model, builder):
|
|
49
|
+
return builder.remove_global_scope(self).update(
|
|
50
|
+
{self.deleted_at_column: None}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def _query_set_null_on_delete(self, builder):
|
|
54
|
+
return builder.set_action("update").set_updates(
|
|
55
|
+
{self.deleted_at_column: builder._model.get_new_datetime_string()}
|
|
56
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .SoftDeleteScope import SoftDeleteScope
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SoftDeletesMixin:
|
|
5
|
+
"""Global scope class to add soft deleting to models."""
|
|
6
|
+
|
|
7
|
+
__deleted_at__ = "deleted_at"
|
|
8
|
+
|
|
9
|
+
def boot_SoftDeletesMixin(self, builder):
|
|
10
|
+
builder.set_global_scope(SoftDeleteScope(self.__deleted_at__))
|
|
11
|
+
|
|
12
|
+
def get_deleted_at_column(self):
|
|
13
|
+
return self.__deleted_at__
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .TimeStampsScope import TimeStampsScope
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TimeStampsMixin:
|
|
5
|
+
"""Global scope class to add soft deleting to models."""
|
|
6
|
+
|
|
7
|
+
def boot_TimeStampsMixin(self, builder):
|
|
8
|
+
builder.set_global_scope(TimeStampsScope())
|
|
9
|
+
|
|
10
|
+
def activate_timestamps(self, boolean=True):
|
|
11
|
+
self.__timestamps__ = boolean
|
|
12
|
+
return self
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from ..expressions.expressions import UpdateQueryExpression
|
|
2
|
+
from .BaseScope import BaseScope
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TimeStampsScope(BaseScope):
|
|
6
|
+
"""Global scope class to add soft deleting to models."""
|
|
7
|
+
|
|
8
|
+
def on_boot(self, builder):
|
|
9
|
+
builder.set_global_scope(
|
|
10
|
+
"_timestamps", self.set_timestamp_create, action="insert"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
builder.set_global_scope(
|
|
14
|
+
"_timestamp_update", self.set_timestamp_update, action="update"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
def on_remove(self, builder):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
def set_timestamp(owner_cls, query):
|
|
21
|
+
owner_cls.updated_at = "now"
|
|
22
|
+
|
|
23
|
+
def set_timestamp_create(self, builder):
|
|
24
|
+
if not builder._model.__timestamps__:
|
|
25
|
+
return builder
|
|
26
|
+
|
|
27
|
+
builder._creates.update(
|
|
28
|
+
{
|
|
29
|
+
builder._model.date_updated_at: builder._model.get_new_date().to_datetime_string(),
|
|
30
|
+
builder._model.date_created_at: builder._model.get_new_date().to_datetime_string(),
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def set_timestamp_update(self, builder):
|
|
35
|
+
if not builder._model.__timestamps__:
|
|
36
|
+
return builder
|
|
37
|
+
|
|
38
|
+
for update in builder._updates:
|
|
39
|
+
if builder._model.date_updated_at in update.column:
|
|
40
|
+
return
|
|
41
|
+
builder._updates += (
|
|
42
|
+
UpdateQueryExpression(
|
|
43
|
+
{
|
|
44
|
+
builder._model.date_updated_at: builder._model.get_new_date().to_datetime_string()
|
|
45
|
+
}
|
|
46
|
+
),
|
|
47
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
from .BaseScope import BaseScope
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class UUIDPrimaryKeyScope(BaseScope):
|
|
7
|
+
"""Global scope class to use UUID4 as primary key."""
|
|
8
|
+
|
|
9
|
+
def on_boot(self, builder):
|
|
10
|
+
builder.set_global_scope(
|
|
11
|
+
"_UUID_primary_key", self.set_uuid_create, action="insert"
|
|
12
|
+
)
|
|
13
|
+
builder.set_global_scope(
|
|
14
|
+
"_UUID_primary_key",
|
|
15
|
+
self.set_bulk_uuid_create,
|
|
16
|
+
action="bulk_create",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
def on_remove(self, builder):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def generate_uuid(self, builder, uuid_version, bytes=False):
|
|
23
|
+
# UUID 3 and 5 requires parameters
|
|
24
|
+
uuid_func = getattr(uuid, f"uuid{uuid_version}")
|
|
25
|
+
args = []
|
|
26
|
+
if uuid_version in [3, 5]:
|
|
27
|
+
args = [
|
|
28
|
+
builder._model.__uuid_namespace__,
|
|
29
|
+
builder._model.__uuid_name__,
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
return uuid_func(*args).bytes if bytes else str(uuid_func(*args))
|
|
33
|
+
|
|
34
|
+
def build_uuid_pk(self, builder):
|
|
35
|
+
uuid_version = getattr(builder._model, "__uuid_version__", 4)
|
|
36
|
+
uuid_bytes = getattr(builder._model, "__uuid_bytes__", False)
|
|
37
|
+
return {
|
|
38
|
+
builder._model.__primary_key__: self.generate_uuid(
|
|
39
|
+
builder, uuid_version, uuid_bytes
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
def set_uuid_create(self, builder):
|
|
44
|
+
# if there is already a primary key, no need to set a new one
|
|
45
|
+
if builder._model.__primary_key__ not in builder._creates:
|
|
46
|
+
builder._creates.update(self.build_uuid_pk(builder))
|
|
47
|
+
|
|
48
|
+
def set_bulk_uuid_create(self, builder):
|
|
49
|
+
for idx, create_atts in enumerate(builder._creates):
|
|
50
|
+
if builder._model.__primary_key__ not in create_atts:
|
|
51
|
+
builder._creates[idx].update(self.build_uuid_pk(builder))
|