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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. masonite_framework_orm-3.0.1.dist-info/METADATA +87 -0
  2. masonite_framework_orm-3.0.1.dist-info/RECORD +116 -0
  3. masonite_framework_orm-3.0.1.dist-info/WHEEL +5 -0
  4. masonite_framework_orm-3.0.1.dist-info/entry_points.txt +3 -0
  5. masonite_framework_orm-3.0.1.dist-info/licenses/LICENSE +21 -0
  6. masonite_framework_orm-3.0.1.dist-info/top_level.txt +1 -0
  7. masoniteorm/__init__.py +1 -0
  8. masoniteorm/collection/Collection.py +605 -0
  9. masoniteorm/collection/__init__.py +1 -0
  10. masoniteorm/commands/CanOverrideConfig.py +16 -0
  11. masoniteorm/commands/CanOverrideOptionsDefault.py +22 -0
  12. masoniteorm/commands/Command.py +6 -0
  13. masoniteorm/commands/Entry.py +43 -0
  14. masoniteorm/commands/MakeMigrationCommand.py +57 -0
  15. masoniteorm/commands/MakeModelCommand.py +78 -0
  16. masoniteorm/commands/MakeModelDocstringCommand.py +37 -0
  17. masoniteorm/commands/MakeObserverCommand.py +54 -0
  18. masoniteorm/commands/MakeSeedCommand.py +54 -0
  19. masoniteorm/commands/MigrateCommand.py +46 -0
  20. masoniteorm/commands/MigrateFreshCommand.py +41 -0
  21. masoniteorm/commands/MigrateRefreshCommand.py +41 -0
  22. masoniteorm/commands/MigrateResetCommand.py +25 -0
  23. masoniteorm/commands/MigrateRollbackCommand.py +26 -0
  24. masoniteorm/commands/MigrateStatusCommand.py +51 -0
  25. masoniteorm/commands/SeedRunCommand.py +35 -0
  26. masoniteorm/commands/ShellCommand.py +205 -0
  27. masoniteorm/commands/__init__.py +18 -0
  28. masoniteorm/commands/stubs/create_migration.stub +20 -0
  29. masoniteorm/commands/stubs/create_seed.stub +9 -0
  30. masoniteorm/commands/stubs/model.stub +9 -0
  31. masoniteorm/commands/stubs/observer.stub +101 -0
  32. masoniteorm/commands/stubs/table_migration.stub +19 -0
  33. masoniteorm/config.py +123 -0
  34. masoniteorm/connections/BaseConnection.py +101 -0
  35. masoniteorm/connections/ConnectionFactory.py +59 -0
  36. masoniteorm/connections/ConnectionResolver.py +132 -0
  37. masoniteorm/connections/MSSQLConnection.py +176 -0
  38. masoniteorm/connections/MySQLConnection.py +232 -0
  39. masoniteorm/connections/PostgresConnection.py +225 -0
  40. masoniteorm/connections/SQLiteConnection.py +179 -0
  41. masoniteorm/connections/__init__.py +6 -0
  42. masoniteorm/exceptions.py +38 -0
  43. masoniteorm/expressions/__init__.py +1 -0
  44. masoniteorm/expressions/expressions.py +288 -0
  45. masoniteorm/factories/Factory.py +112 -0
  46. masoniteorm/factories/__init__.py +1 -0
  47. masoniteorm/helpers/__init__.py +0 -0
  48. masoniteorm/helpers/misc.py +22 -0
  49. masoniteorm/migrations/Migration.py +330 -0
  50. masoniteorm/migrations/__init__.py +1 -0
  51. masoniteorm/models/MigrationModel.py +9 -0
  52. masoniteorm/models/Model.py +1209 -0
  53. masoniteorm/models/Model.pyi +1366 -0
  54. masoniteorm/models/Pivot.py +5 -0
  55. masoniteorm/models/__init__.py +1 -0
  56. masoniteorm/observers/ObservesEvents.py +27 -0
  57. masoniteorm/observers/__init__.py +1 -0
  58. masoniteorm/pagination/BasePaginator.py +10 -0
  59. masoniteorm/pagination/LengthAwarePaginator.py +34 -0
  60. masoniteorm/pagination/SimplePaginator.py +28 -0
  61. masoniteorm/pagination/__init__.py +2 -0
  62. masoniteorm/providers/ORMProvider.py +39 -0
  63. masoniteorm/providers/__init__.py +1 -0
  64. masoniteorm/query/EagerRelation.py +42 -0
  65. masoniteorm/query/QueryBuilder.py +2486 -0
  66. masoniteorm/query/__init__.py +1 -0
  67. masoniteorm/query/grammars/BaseGrammar.py +1027 -0
  68. masoniteorm/query/grammars/MSSQLGrammar.py +194 -0
  69. masoniteorm/query/grammars/MySQLGrammar.py +238 -0
  70. masoniteorm/query/grammars/PostgresGrammar.py +213 -0
  71. masoniteorm/query/grammars/SQLiteGrammar.py +228 -0
  72. masoniteorm/query/grammars/__init__.py +4 -0
  73. masoniteorm/query/processors/MSSQLPostProcessor.py +58 -0
  74. masoniteorm/query/processors/MySQLPostProcessor.py +48 -0
  75. masoniteorm/query/processors/PostgresPostProcessor.py +49 -0
  76. masoniteorm/query/processors/SQLitePostProcessor.py +49 -0
  77. masoniteorm/query/processors/__init__.py +4 -0
  78. masoniteorm/relationships/BaseRelationship.py +161 -0
  79. masoniteorm/relationships/BelongsTo.py +124 -0
  80. masoniteorm/relationships/BelongsToMany.py +604 -0
  81. masoniteorm/relationships/HasMany.py +66 -0
  82. masoniteorm/relationships/HasManyThrough.py +269 -0
  83. masoniteorm/relationships/HasOne.py +111 -0
  84. masoniteorm/relationships/HasOneThrough.py +275 -0
  85. masoniteorm/relationships/MorphMany.py +152 -0
  86. masoniteorm/relationships/MorphOne.py +156 -0
  87. masoniteorm/relationships/MorphTo.py +111 -0
  88. masoniteorm/relationships/MorphToMany.py +108 -0
  89. masoniteorm/relationships/__init__.py +10 -0
  90. masoniteorm/schema/Blueprint.py +1161 -0
  91. masoniteorm/schema/Column.py +144 -0
  92. masoniteorm/schema/ColumnDiff.py +0 -0
  93. masoniteorm/schema/Constraint.py +5 -0
  94. masoniteorm/schema/ForeignKeyConstraint.py +28 -0
  95. masoniteorm/schema/Index.py +5 -0
  96. masoniteorm/schema/Schema.py +359 -0
  97. masoniteorm/schema/Table.py +94 -0
  98. masoniteorm/schema/TableDiff.py +86 -0
  99. masoniteorm/schema/__init__.py +3 -0
  100. masoniteorm/schema/platforms/MSSQLPlatform.py +367 -0
  101. masoniteorm/schema/platforms/MySQLPlatform.py +513 -0
  102. masoniteorm/schema/platforms/Platform.py +97 -0
  103. masoniteorm/schema/platforms/PostgresPlatform.py +551 -0
  104. masoniteorm/schema/platforms/SQLitePlatform.py +481 -0
  105. masoniteorm/schema/platforms/__init__.py +4 -0
  106. masoniteorm/scopes/BaseScope.py +6 -0
  107. masoniteorm/scopes/SoftDeleteScope.py +56 -0
  108. masoniteorm/scopes/SoftDeletesMixin.py +13 -0
  109. masoniteorm/scopes/TimeStampsMixin.py +12 -0
  110. masoniteorm/scopes/TimeStampsScope.py +47 -0
  111. masoniteorm/scopes/UUIDPrimaryKeyMixin.py +8 -0
  112. masoniteorm/scopes/UUIDPrimaryKeyScope.py +51 -0
  113. masoniteorm/scopes/__init__.py +8 -0
  114. masoniteorm/scopes/scope.py +15 -0
  115. masoniteorm/seeds/Seeder.py +42 -0
  116. masoniteorm/seeds/__init__.py +1 -0
@@ -0,0 +1,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,4 @@
1
+ from .MSSQLPlatform import MSSQLPlatform
2
+ from .MySQLPlatform import MySQLPlatform
3
+ from .PostgresPlatform import PostgresPlatform
4
+ from .SQLitePlatform import SQLitePlatform
@@ -0,0 +1,6 @@
1
+ class BaseScope:
2
+ def on_boot(self, builder):
3
+ raise NotImplementedError()
4
+
5
+ def on_remove(self, builder):
6
+ raise NotImplementedError()
@@ -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,8 @@
1
+ from .UUIDPrimaryKeyScope import UUIDPrimaryKeyScope
2
+
3
+
4
+ class UUIDPrimaryKeyMixin:
5
+ """Global scope class to add UUID as primary key to models."""
6
+
7
+ def boot_UUIDPrimaryKeyMixin(self, builder):
8
+ builder.set_global_scope(UUIDPrimaryKeyScope())
@@ -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))