plain.models 0.49.2__py3-none-any.whl → 0.51.0__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 (108) hide show
  1. plain/models/CHANGELOG.md +27 -0
  2. plain/models/README.md +26 -42
  3. plain/models/__init__.py +2 -0
  4. plain/models/aggregates.py +42 -19
  5. plain/models/backends/base/base.py +125 -105
  6. plain/models/backends/base/client.py +11 -3
  7. plain/models/backends/base/creation.py +24 -14
  8. plain/models/backends/base/features.py +10 -4
  9. plain/models/backends/base/introspection.py +37 -20
  10. plain/models/backends/base/operations.py +187 -91
  11. plain/models/backends/base/schema.py +338 -218
  12. plain/models/backends/base/validation.py +13 -4
  13. plain/models/backends/ddl_references.py +85 -43
  14. plain/models/backends/mysql/base.py +29 -26
  15. plain/models/backends/mysql/client.py +7 -2
  16. plain/models/backends/mysql/compiler.py +13 -4
  17. plain/models/backends/mysql/creation.py +5 -2
  18. plain/models/backends/mysql/features.py +24 -22
  19. plain/models/backends/mysql/introspection.py +22 -13
  20. plain/models/backends/mysql/operations.py +107 -40
  21. plain/models/backends/mysql/schema.py +52 -28
  22. plain/models/backends/mysql/validation.py +13 -6
  23. plain/models/backends/postgresql/base.py +41 -34
  24. plain/models/backends/postgresql/client.py +7 -2
  25. plain/models/backends/postgresql/creation.py +10 -5
  26. plain/models/backends/postgresql/introspection.py +15 -8
  27. plain/models/backends/postgresql/operations.py +110 -43
  28. plain/models/backends/postgresql/schema.py +88 -49
  29. plain/models/backends/sqlite3/_functions.py +151 -115
  30. plain/models/backends/sqlite3/base.py +37 -23
  31. plain/models/backends/sqlite3/client.py +7 -1
  32. plain/models/backends/sqlite3/creation.py +9 -5
  33. plain/models/backends/sqlite3/features.py +5 -3
  34. plain/models/backends/sqlite3/introspection.py +32 -16
  35. plain/models/backends/sqlite3/operations.py +126 -43
  36. plain/models/backends/sqlite3/schema.py +127 -92
  37. plain/models/backends/utils.py +52 -29
  38. plain/models/backups/cli.py +8 -6
  39. plain/models/backups/clients.py +16 -7
  40. plain/models/backups/core.py +24 -13
  41. plain/models/base.py +221 -229
  42. plain/models/cli.py +98 -67
  43. plain/models/config.py +1 -1
  44. plain/models/connections.py +23 -7
  45. plain/models/constraints.py +79 -56
  46. plain/models/database_url.py +1 -1
  47. plain/models/db.py +6 -2
  48. plain/models/deletion.py +80 -56
  49. plain/models/entrypoints.py +1 -1
  50. plain/models/enums.py +22 -11
  51. plain/models/exceptions.py +23 -8
  52. plain/models/expressions.py +441 -258
  53. plain/models/fields/__init__.py +272 -217
  54. plain/models/fields/json.py +123 -57
  55. plain/models/fields/mixins.py +12 -8
  56. plain/models/fields/related.py +324 -290
  57. plain/models/fields/related_descriptors.py +33 -24
  58. plain/models/fields/related_lookups.py +24 -12
  59. plain/models/fields/related_managers.py +102 -79
  60. plain/models/fields/reverse_related.py +66 -63
  61. plain/models/forms.py +101 -75
  62. plain/models/functions/comparison.py +71 -18
  63. plain/models/functions/datetime.py +79 -29
  64. plain/models/functions/math.py +43 -10
  65. plain/models/functions/mixins.py +24 -7
  66. plain/models/functions/text.py +104 -25
  67. plain/models/functions/window.py +12 -6
  68. plain/models/indexes.py +57 -32
  69. plain/models/lookups.py +228 -153
  70. plain/models/meta.py +505 -0
  71. plain/models/migrations/autodetector.py +86 -43
  72. plain/models/migrations/exceptions.py +7 -3
  73. plain/models/migrations/executor.py +33 -7
  74. plain/models/migrations/graph.py +79 -50
  75. plain/models/migrations/loader.py +45 -22
  76. plain/models/migrations/migration.py +23 -18
  77. plain/models/migrations/operations/base.py +38 -20
  78. plain/models/migrations/operations/fields.py +95 -48
  79. plain/models/migrations/operations/models.py +246 -142
  80. plain/models/migrations/operations/special.py +82 -25
  81. plain/models/migrations/optimizer.py +7 -2
  82. plain/models/migrations/questioner.py +58 -31
  83. plain/models/migrations/recorder.py +27 -16
  84. plain/models/migrations/serializer.py +50 -39
  85. plain/models/migrations/state.py +232 -156
  86. plain/models/migrations/utils.py +30 -14
  87. plain/models/migrations/writer.py +17 -14
  88. plain/models/options.py +189 -518
  89. plain/models/otel.py +16 -6
  90. plain/models/preflight.py +42 -17
  91. plain/models/query.py +400 -251
  92. plain/models/query_utils.py +109 -69
  93. plain/models/registry.py +40 -21
  94. plain/models/sql/compiler.py +190 -127
  95. plain/models/sql/datastructures.py +38 -25
  96. plain/models/sql/query.py +320 -225
  97. plain/models/sql/subqueries.py +36 -25
  98. plain/models/sql/where.py +54 -29
  99. plain/models/test/pytest.py +15 -11
  100. plain/models/test/utils.py +4 -2
  101. plain/models/transaction.py +20 -7
  102. plain/models/utils.py +17 -6
  103. {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/METADATA +27 -43
  104. plain_models-0.51.0.dist-info/RECORD +123 -0
  105. plain_models-0.49.2.dist-info/RECORD +0 -122
  106. {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/WHEEL +0 -0
  107. {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/entry_points.txt +0 -0
  108. {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,14 +1,23 @@
1
+ from __future__ import annotations
2
+
1
3
  from functools import cached_property
4
+ from typing import TYPE_CHECKING, Any
2
5
 
3
6
  from plain import models
7
+ from plain.models.base import ModelBase
4
8
  from plain.models.migrations.operations.base import Operation
5
9
  from plain.models.migrations.state import ModelState
6
10
  from plain.models.migrations.utils import field_references, resolve_relation
7
11
 
8
12
  from .fields import AddField, AlterField, FieldOperation, RemoveField, RenameField
9
13
 
14
+ if TYPE_CHECKING:
15
+ from plain.models.backends.base.schema import BaseDatabaseSchemaEditor
16
+ from plain.models.fields import Field
17
+ from plain.models.migrations.state import ProjectState
18
+
10
19
 
11
- def _check_for_duplicates(arg_name, objs):
20
+ def _check_for_duplicates(arg_name: str, objs: Any) -> None:
12
21
  used_vals = set()
13
22
  for val in objs:
14
23
  if val in used_vals:
@@ -19,22 +28,24 @@ def _check_for_duplicates(arg_name, objs):
19
28
 
20
29
 
21
30
  class ModelOperation(Operation):
22
- def __init__(self, name):
31
+ def __init__(self, name: str) -> None:
23
32
  self.name = name
24
33
 
25
34
  @cached_property
26
- def name_lower(self):
35
+ def name_lower(self) -> str:
27
36
  return self.name.lower()
28
37
 
29
- def references_model(self, name, package_label):
38
+ def references_model(self, name: str, package_label: str) -> bool:
30
39
  return name.lower() == self.name_lower
31
40
 
32
- def reduce(self, operation, package_label):
33
- return super().reduce(operation, package_label) or self.can_reduce_through(
41
+ def reduce(
42
+ self, operation: Operation, package_label: str
43
+ ) -> bool | list[Operation]:
44
+ return super().reduce(operation, package_label) or self.can_reduce_through( # type: ignore[misc]
34
45
  operation, package_label
35
46
  )
36
47
 
37
- def can_reduce_through(self, operation, package_label):
48
+ def can_reduce_through(self, operation: Operation, package_label: str) -> bool:
38
49
  return not operation.references_model(self.name, package_label)
39
50
 
40
51
 
@@ -43,7 +54,13 @@ class CreateModel(ModelOperation):
43
54
 
44
55
  serialization_expand_args = ["fields", "options"]
45
56
 
46
- def __init__(self, name, fields, options=None, bases=None):
57
+ def __init__(
58
+ self,
59
+ name: str,
60
+ fields: list[tuple[str, Field]],
61
+ options: dict[str, Any] | None = None,
62
+ bases: tuple[Any, ...] | None = None,
63
+ ) -> None:
47
64
  self.fields = fields
48
65
  self.options = options or {}
49
66
  self.bases = bases or (models.Model,)
@@ -53,8 +70,10 @@ class CreateModel(ModelOperation):
53
70
  _check_for_duplicates(
54
71
  "bases",
55
72
  (
56
- base._meta.label_lower
57
- if hasattr(base, "_meta")
73
+ base.model_options.label_lower
74
+ if not isinstance(base, str)
75
+ and base is not models.Model
76
+ and hasattr(base, "_model_meta")
58
77
  else base.lower()
59
78
  if isinstance(base, str)
60
79
  else base
@@ -62,8 +81,8 @@ class CreateModel(ModelOperation):
62
81
  ),
63
82
  )
64
83
 
65
- def deconstruct(self):
66
- kwargs = {
84
+ def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]:
85
+ kwargs: dict[str, Any] = {
67
86
  "name": self.name,
68
87
  "fields": self.fields,
69
88
  }
@@ -73,7 +92,7 @@ class CreateModel(ModelOperation):
73
92
  kwargs["bases"] = self.bases
74
93
  return (self.__class__.__qualname__, [], kwargs)
75
94
 
76
- def state_forwards(self, package_label, state):
95
+ def state_forwards(self, package_label: str, state: ProjectState) -> None:
77
96
  state.add_model(
78
97
  ModelState(
79
98
  package_label,
@@ -84,19 +103,25 @@ class CreateModel(ModelOperation):
84
103
  )
85
104
  )
86
105
 
87
- def database_forwards(self, package_label, schema_editor, from_state, to_state):
88
- model = to_state.models_registry.get_model(package_label, self.name)
106
+ def database_forwards(
107
+ self,
108
+ package_label: str,
109
+ schema_editor: BaseDatabaseSchemaEditor,
110
+ from_state: ProjectState,
111
+ to_state: ProjectState,
112
+ ) -> None:
113
+ model = to_state.models_registry.get_model(package_label, self.name) # type: ignore[attr-defined]
89
114
  if self.allow_migrate_model(schema_editor.connection, model):
90
115
  schema_editor.create_model(model)
91
116
 
92
- def describe(self):
117
+ def describe(self) -> str:
93
118
  return f"Create model {self.name}"
94
119
 
95
120
  @property
96
- def migration_name_fragment(self):
121
+ def migration_name_fragment(self) -> str:
97
122
  return self.name_lower
98
123
 
99
- def references_model(self, name, package_label):
124
+ def references_model(self, name: str, package_label: str) -> bool:
100
125
  name_lower = name.lower()
101
126
  if name_lower == self.name_lower:
102
127
  return True
@@ -106,7 +131,7 @@ class CreateModel(ModelOperation):
106
131
  for base in self.bases:
107
132
  if (
108
133
  base is not models.Model
109
- and isinstance(base, models.base.ModelBase | str)
134
+ and isinstance(base, ModelBase | str)
110
135
  and resolve_relation(base, package_label) == reference_model_tuple
111
136
  ):
112
137
  return True
@@ -119,7 +144,9 @@ class CreateModel(ModelOperation):
119
144
  return True
120
145
  return False
121
146
 
122
- def reduce(self, operation, package_label):
147
+ def reduce(
148
+ self, operation: Operation, package_label: str
149
+ ) -> bool | list[Operation]:
123
150
  if (
124
151
  isinstance(operation, DeleteModel)
125
152
  and self.name_lower == operation.name_lower
@@ -213,104 +240,118 @@ class CreateModel(ModelOperation):
213
240
  class DeleteModel(ModelOperation):
214
241
  """Drop a model's table."""
215
242
 
216
- def deconstruct(self):
217
- kwargs = {
243
+ def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]:
244
+ kwargs: dict[str, Any] = {
218
245
  "name": self.name,
219
246
  }
220
247
  return (self.__class__.__qualname__, [], kwargs)
221
248
 
222
- def state_forwards(self, package_label, state):
249
+ def state_forwards(self, package_label: str, state: ProjectState) -> None:
223
250
  state.remove_model(package_label, self.name_lower)
224
251
 
225
- def database_forwards(self, package_label, schema_editor, from_state, to_state):
226
- model = from_state.models_registry.get_model(package_label, self.name)
252
+ def database_forwards(
253
+ self,
254
+ package_label: str,
255
+ schema_editor: BaseDatabaseSchemaEditor,
256
+ from_state: ProjectState,
257
+ to_state: ProjectState,
258
+ ) -> None:
259
+ model = from_state.models_registry.get_model(package_label, self.name) # type: ignore[attr-defined]
227
260
  if self.allow_migrate_model(schema_editor.connection, model):
228
261
  schema_editor.delete_model(model)
229
262
 
230
- def references_model(self, name, package_label):
263
+ def references_model(self, name: str, package_label: str) -> bool:
231
264
  # The deleted model could be referencing the specified model through
232
265
  # related fields.
233
266
  return True
234
267
 
235
- def describe(self):
268
+ def describe(self) -> str:
236
269
  return f"Delete model {self.name}"
237
270
 
238
271
  @property
239
- def migration_name_fragment(self):
272
+ def migration_name_fragment(self) -> str:
240
273
  return f"delete_{self.name_lower}"
241
274
 
242
275
 
243
276
  class RenameModel(ModelOperation):
244
277
  """Rename a model."""
245
278
 
246
- def __init__(self, old_name, new_name):
279
+ def __init__(self, old_name: str, new_name: str) -> None:
247
280
  self.old_name = old_name
248
281
  self.new_name = new_name
249
282
  super().__init__(old_name)
250
283
 
251
284
  @cached_property
252
- def old_name_lower(self):
285
+ def old_name_lower(self) -> str:
253
286
  return self.old_name.lower()
254
287
 
255
288
  @cached_property
256
- def new_name_lower(self):
289
+ def new_name_lower(self) -> str:
257
290
  return self.new_name.lower()
258
291
 
259
- def deconstruct(self):
260
- kwargs = {
292
+ def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]:
293
+ kwargs: dict[str, Any] = {
261
294
  "old_name": self.old_name,
262
295
  "new_name": self.new_name,
263
296
  }
264
297
  return (self.__class__.__qualname__, [], kwargs)
265
298
 
266
- def state_forwards(self, package_label, state):
299
+ def state_forwards(self, package_label: str, state: ProjectState) -> None:
267
300
  state.rename_model(package_label, self.old_name, self.new_name)
268
301
 
269
- def database_forwards(self, package_label, schema_editor, from_state, to_state):
270
- new_model = to_state.models_registry.get_model(package_label, self.new_name)
302
+ def database_forwards(
303
+ self,
304
+ package_label: str,
305
+ schema_editor: BaseDatabaseSchemaEditor,
306
+ from_state: ProjectState,
307
+ to_state: ProjectState,
308
+ ) -> None:
309
+ new_model = to_state.models_registry.get_model(package_label, self.new_name) # type: ignore[attr-defined]
271
310
  if self.allow_migrate_model(schema_editor.connection, new_model):
272
- old_model = from_state.models_registry.get_model(
311
+ old_model = from_state.models_registry.get_model( # type: ignore[attr-defined]
273
312
  package_label, self.old_name
274
313
  )
275
314
  # Move the main table
276
315
  schema_editor.alter_db_table(
277
316
  new_model,
278
- old_model._meta.db_table,
279
- new_model._meta.db_table,
317
+ old_model.model_options.db_table,
318
+ new_model.model_options.db_table,
280
319
  )
281
320
  # Alter the fields pointing to us
282
- for related_object in old_model._meta.related_objects:
283
- if related_object.related_model == old_model:
321
+ for related_object in old_model._model_meta.related_objects:
322
+ if related_object.related_model == old_model: # type: ignore[attr-defined]
284
323
  model = new_model
285
324
  related_key = (package_label, self.new_name_lower)
286
325
  else:
287
- model = related_object.related_model
326
+ model = related_object.related_model # type: ignore[attr-defined]
288
327
  related_key = (
289
- related_object.related_model._meta.package_label,
290
- related_object.related_model._meta.model_name,
328
+ related_object.related_model.model_options.package_label,
329
+ related_object.related_model.model_options.model_name,
291
330
  )
292
331
  to_field = to_state.models_registry.get_model(
293
332
  *related_key
294
- )._meta.get_field(related_object.field.name)
333
+ )._model_meta.get_field(related_object.field.name)
295
334
  schema_editor.alter_field(
296
335
  model,
297
336
  related_object.field,
298
337
  to_field,
299
338
  )
300
339
 
301
- def references_model(self, name, package_label):
340
+ def references_model(self, name: str, package_label: str) -> bool:
302
341
  return (
303
342
  name.lower() == self.old_name_lower or name.lower() == self.new_name_lower
304
343
  )
305
344
 
306
- def describe(self):
345
+ def describe(self) -> str:
307
346
  return f"Rename model {self.old_name} to {self.new_name}"
308
347
 
309
348
  @property
310
- def migration_name_fragment(self):
349
+ def migration_name_fragment(self) -> str:
311
350
  return f"rename_{self.old_name_lower}_{self.new_name_lower}"
312
351
 
313
- def reduce(self, operation, package_label):
352
+ def reduce(
353
+ self, operation: Operation, package_label: str
354
+ ) -> bool | list[Operation]:
314
355
  if (
315
356
  isinstance(operation, RenameModel)
316
357
  and self.new_name_lower == operation.old_name_lower
@@ -323,13 +364,15 @@ class RenameModel(ModelOperation):
323
364
  ]
324
365
  # Skip `ModelOperation.reduce` as we want to run `references_model`
325
366
  # against self.new_name.
326
- return super(ModelOperation, self).reduce(
367
+ return super(ModelOperation, self).reduce( # type: ignore[misc]
327
368
  operation, package_label
328
369
  ) or not operation.references_model(self.new_name, package_label)
329
370
 
330
371
 
331
372
  class ModelOptionOperation(ModelOperation):
332
- def reduce(self, operation, package_label):
373
+ def reduce(
374
+ self, operation: Operation, package_label: str
375
+ ) -> bool | list[Operation]:
333
376
  if (
334
377
  isinstance(operation, self.__class__ | DeleteModel)
335
378
  and self.name_lower == operation.name_lower
@@ -341,75 +384,87 @@ class ModelOptionOperation(ModelOperation):
341
384
  class AlterModelTable(ModelOptionOperation):
342
385
  """Rename a model's table."""
343
386
 
344
- def __init__(self, name, table):
387
+ def __init__(self, name: str, table: str | None) -> None:
345
388
  self.table = table
346
389
  super().__init__(name)
347
390
 
348
- def deconstruct(self):
349
- kwargs = {
391
+ def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]:
392
+ kwargs: dict[str, Any] = {
350
393
  "name": self.name,
351
394
  "table": self.table,
352
395
  }
353
396
  return (self.__class__.__qualname__, [], kwargs)
354
397
 
355
- def state_forwards(self, package_label, state):
398
+ def state_forwards(self, package_label: str, state: ProjectState) -> None:
356
399
  state.alter_model_options(
357
400
  package_label, self.name_lower, {"db_table": self.table}
358
401
  )
359
402
 
360
- def database_forwards(self, package_label, schema_editor, from_state, to_state):
361
- new_model = to_state.models_registry.get_model(package_label, self.name)
403
+ def database_forwards(
404
+ self,
405
+ package_label: str,
406
+ schema_editor: BaseDatabaseSchemaEditor,
407
+ from_state: ProjectState,
408
+ to_state: ProjectState,
409
+ ) -> None:
410
+ new_model = to_state.models_registry.get_model(package_label, self.name) # type: ignore[attr-defined]
362
411
  if self.allow_migrate_model(schema_editor.connection, new_model):
363
- old_model = from_state.models_registry.get_model(package_label, self.name)
412
+ old_model = from_state.models_registry.get_model(package_label, self.name) # type: ignore[attr-defined]
364
413
  schema_editor.alter_db_table(
365
414
  new_model,
366
- old_model._meta.db_table,
367
- new_model._meta.db_table,
415
+ old_model.model_options.db_table,
416
+ new_model.model_options.db_table,
368
417
  )
369
418
 
370
- def describe(self):
419
+ def describe(self) -> str:
371
420
  return "Rename table for {} to {}".format(
372
421
  self.name,
373
422
  self.table if self.table is not None else "(default)",
374
423
  )
375
424
 
376
425
  @property
377
- def migration_name_fragment(self):
426
+ def migration_name_fragment(self) -> str:
378
427
  return f"alter_{self.name_lower}_table"
379
428
 
380
429
 
381
430
  class AlterModelTableComment(ModelOptionOperation):
382
- def __init__(self, name, table_comment):
431
+ def __init__(self, name: str, table_comment: str | None) -> None:
383
432
  self.table_comment = table_comment
384
433
  super().__init__(name)
385
434
 
386
- def deconstruct(self):
387
- kwargs = {
435
+ def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]:
436
+ kwargs: dict[str, Any] = {
388
437
  "name": self.name,
389
438
  "table_comment": self.table_comment,
390
439
  }
391
440
  return (self.__class__.__qualname__, [], kwargs)
392
441
 
393
- def state_forwards(self, package_label, state):
442
+ def state_forwards(self, package_label: str, state: ProjectState) -> None:
394
443
  state.alter_model_options(
395
444
  package_label, self.name_lower, {"db_table_comment": self.table_comment}
396
445
  )
397
446
 
398
- def database_forwards(self, package_label, schema_editor, from_state, to_state):
399
- new_model = to_state.models_registry.get_model(package_label, self.name)
447
+ def database_forwards(
448
+ self,
449
+ package_label: str,
450
+ schema_editor: BaseDatabaseSchemaEditor,
451
+ from_state: ProjectState,
452
+ to_state: ProjectState,
453
+ ) -> None:
454
+ new_model = to_state.models_registry.get_model(package_label, self.name) # type: ignore[attr-defined]
400
455
  if self.allow_migrate_model(schema_editor.connection, new_model):
401
- old_model = from_state.models_registry.get_model(package_label, self.name)
456
+ old_model = from_state.models_registry.get_model(package_label, self.name) # type: ignore[attr-defined]
402
457
  schema_editor.alter_db_table_comment(
403
458
  new_model,
404
- old_model._meta.db_table_comment,
405
- new_model._meta.db_table_comment,
459
+ old_model.model_options.db_table_comment,
460
+ new_model.model_options.db_table_comment,
406
461
  )
407
462
 
408
- def describe(self):
463
+ def describe(self) -> str:
409
464
  return f"Alter {self.name} table comment"
410
465
 
411
466
  @property
412
- def migration_name_fragment(self):
467
+ def migration_name_fragment(self) -> str:
413
468
  return f"alter_{self.name_lower}_table_comment"
414
469
 
415
470
 
@@ -425,18 +480,18 @@ class AlterModelOptions(ModelOptionOperation):
425
480
  "ordering",
426
481
  ]
427
482
 
428
- def __init__(self, name, options):
483
+ def __init__(self, name: str, options: dict[str, Any]) -> None:
429
484
  self.options = options
430
485
  super().__init__(name)
431
486
 
432
- def deconstruct(self):
433
- kwargs = {
487
+ def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]:
488
+ kwargs: dict[str, Any] = {
434
489
  "name": self.name,
435
490
  "options": self.options,
436
491
  }
437
492
  return (self.__class__.__qualname__, [], kwargs)
438
493
 
439
- def state_forwards(self, package_label, state):
494
+ def state_forwards(self, package_label: str, state: ProjectState) -> None:
440
495
  state.alter_model_options(
441
496
  package_label,
442
497
  self.name_lower,
@@ -444,14 +499,20 @@ class AlterModelOptions(ModelOptionOperation):
444
499
  self.ALTER_OPTION_KEYS,
445
500
  )
446
501
 
447
- def database_forwards(self, package_label, schema_editor, from_state, to_state):
502
+ def database_forwards(
503
+ self,
504
+ package_label: str,
505
+ schema_editor: BaseDatabaseSchemaEditor,
506
+ from_state: ProjectState,
507
+ to_state: ProjectState,
508
+ ) -> None:
448
509
  pass
449
510
 
450
- def describe(self):
511
+ def describe(self) -> str:
451
512
  return f"Change Meta options on {self.name}"
452
513
 
453
514
  @property
454
- def migration_name_fragment(self):
515
+ def migration_name_fragment(self) -> str:
455
516
  return f"alter_{self.name_lower}_options"
456
517
 
457
518
 
@@ -459,32 +520,38 @@ class IndexOperation(Operation):
459
520
  option_name = "indexes"
460
521
 
461
522
  @cached_property
462
- def model_name_lower(self):
463
- return self.model_name.lower()
523
+ def model_name_lower(self) -> str:
524
+ return self.model_name.lower() # type: ignore[attr-defined]
464
525
 
465
526
 
466
527
  class AddIndex(IndexOperation):
467
528
  """Add an index on a model."""
468
529
 
469
- def __init__(self, model_name, index):
530
+ def __init__(self, model_name: str, index: Any) -> None:
470
531
  self.model_name = model_name
471
- if not index.name:
532
+ if not index.name: # type: ignore[attr-defined]
472
533
  raise ValueError(
473
534
  "Indexes passed to AddIndex operations require a name "
474
535
  f"argument. {index!r} doesn't have one."
475
536
  )
476
537
  self.index = index
477
538
 
478
- def state_forwards(self, package_label, state):
539
+ def state_forwards(self, package_label: str, state: ProjectState) -> None:
479
540
  state.add_index(package_label, self.model_name_lower, self.index)
480
541
 
481
- def database_forwards(self, package_label, schema_editor, from_state, to_state):
482
- model = to_state.models_registry.get_model(package_label, self.model_name)
542
+ def database_forwards(
543
+ self,
544
+ package_label: str,
545
+ schema_editor: BaseDatabaseSchemaEditor,
546
+ from_state: ProjectState,
547
+ to_state: ProjectState,
548
+ ) -> None:
549
+ model = to_state.models_registry.get_model(package_label, self.model_name) # type: ignore[attr-defined]
483
550
  if self.allow_migrate_model(schema_editor.connection, model):
484
551
  schema_editor.add_index(model, self.index)
485
552
 
486
- def deconstruct(self):
487
- kwargs = {
553
+ def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]:
554
+ kwargs: dict[str, Any] = {
488
555
  "model_name": self.model_name,
489
556
  "index": self.index,
490
557
  }
@@ -494,43 +561,49 @@ class AddIndex(IndexOperation):
494
561
  kwargs,
495
562
  )
496
563
 
497
- def describe(self):
498
- if self.index.expressions:
564
+ def describe(self) -> str:
565
+ if self.index.expressions: # type: ignore[attr-defined]
499
566
  return "Create index {} on {} on model {}".format(
500
- self.index.name,
501
- ", ".join([str(expression) for expression in self.index.expressions]),
567
+ self.index.name, # type: ignore[attr-defined]
568
+ ", ".join([str(expression) for expression in self.index.expressions]), # type: ignore[attr-defined]
502
569
  self.model_name,
503
570
  )
504
571
  return "Create index {} on field(s) {} of model {}".format(
505
- self.index.name,
506
- ", ".join(self.index.fields),
572
+ self.index.name, # type: ignore[attr-defined]
573
+ ", ".join(self.index.fields), # type: ignore[attr-defined]
507
574
  self.model_name,
508
575
  )
509
576
 
510
577
  @property
511
- def migration_name_fragment(self):
512
- return f"{self.model_name_lower}_{self.index.name.lower()}"
578
+ def migration_name_fragment(self) -> str:
579
+ return f"{self.model_name_lower}_{self.index.name.lower()}" # type: ignore[attr-defined]
513
580
 
514
581
 
515
582
  class RemoveIndex(IndexOperation):
516
583
  """Remove an index from a model."""
517
584
 
518
- def __init__(self, model_name, name):
585
+ def __init__(self, model_name: str, name: str) -> None:
519
586
  self.model_name = model_name
520
587
  self.name = name
521
588
 
522
- def state_forwards(self, package_label, state):
589
+ def state_forwards(self, package_label: str, state: ProjectState) -> None:
523
590
  state.remove_index(package_label, self.model_name_lower, self.name)
524
591
 
525
- def database_forwards(self, package_label, schema_editor, from_state, to_state):
526
- model = from_state.models_registry.get_model(package_label, self.model_name)
592
+ def database_forwards(
593
+ self,
594
+ package_label: str,
595
+ schema_editor: BaseDatabaseSchemaEditor,
596
+ from_state: ProjectState,
597
+ to_state: ProjectState,
598
+ ) -> None:
599
+ model = from_state.models_registry.get_model(package_label, self.model_name) # type: ignore[attr-defined]
527
600
  if self.allow_migrate_model(schema_editor.connection, model):
528
601
  from_model_state = from_state.models[package_label, self.model_name_lower]
529
602
  index = from_model_state.get_index_by_name(self.name)
530
603
  schema_editor.remove_index(model, index)
531
604
 
532
- def deconstruct(self):
533
- kwargs = {
605
+ def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]:
606
+ kwargs: dict[str, Any] = {
534
607
  "model_name": self.model_name,
535
608
  "name": self.name,
536
609
  }
@@ -540,18 +613,24 @@ class RemoveIndex(IndexOperation):
540
613
  kwargs,
541
614
  )
542
615
 
543
- def describe(self):
616
+ def describe(self) -> str:
544
617
  return f"Remove index {self.name} from {self.model_name}"
545
618
 
546
619
  @property
547
- def migration_name_fragment(self):
620
+ def migration_name_fragment(self) -> str:
548
621
  return f"remove_{self.model_name_lower}_{self.name.lower()}"
549
622
 
550
623
 
551
624
  class RenameIndex(IndexOperation):
552
625
  """Rename an index."""
553
626
 
554
- def __init__(self, model_name, new_name, old_name=None, old_fields=None):
627
+ def __init__(
628
+ self,
629
+ model_name: str,
630
+ new_name: str,
631
+ old_name: str | None = None,
632
+ old_fields: list[str] | tuple[str, ...] | None = None,
633
+ ) -> None:
555
634
  if not old_name and not old_fields:
556
635
  raise ValueError(
557
636
  "RenameIndex requires one of old_name and old_fields arguments to be "
@@ -567,15 +646,15 @@ class RenameIndex(IndexOperation):
567
646
  self.old_fields = old_fields
568
647
 
569
648
  @cached_property
570
- def old_name_lower(self):
571
- return self.old_name.lower()
649
+ def old_name_lower(self) -> str:
650
+ return self.old_name.lower() # type: ignore[union-attr]
572
651
 
573
652
  @cached_property
574
- def new_name_lower(self):
653
+ def new_name_lower(self) -> str:
575
654
  return self.new_name.lower()
576
655
 
577
- def deconstruct(self):
578
- kwargs = {
656
+ def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]:
657
+ kwargs: dict[str, Any] = {
579
658
  "model_name": self.model_name,
580
659
  "new_name": self.new_name,
581
660
  }
@@ -585,7 +664,7 @@ class RenameIndex(IndexOperation):
585
664
  kwargs["old_fields"] = self.old_fields
586
665
  return (self.__class__.__qualname__, [], kwargs)
587
666
 
588
- def state_forwards(self, package_label, state):
667
+ def state_forwards(self, package_label: str, state: ProjectState) -> None:
589
668
  if self.old_fields:
590
669
  state.add_index(
591
670
  package_label,
@@ -594,20 +673,30 @@ class RenameIndex(IndexOperation):
594
673
  )
595
674
  else:
596
675
  state.rename_index(
597
- package_label, self.model_name_lower, self.old_name, self.new_name
676
+ package_label,
677
+ self.model_name_lower,
678
+ self.old_name,
679
+ self.new_name, # type: ignore[arg-type]
598
680
  )
599
681
 
600
- def database_forwards(self, package_label, schema_editor, from_state, to_state):
601
- model = to_state.models_registry.get_model(package_label, self.model_name)
682
+ def database_forwards(
683
+ self,
684
+ package_label: str,
685
+ schema_editor: BaseDatabaseSchemaEditor,
686
+ from_state: ProjectState,
687
+ to_state: ProjectState,
688
+ ) -> None:
689
+ model = to_state.models_registry.get_model(package_label, self.model_name) # type: ignore[attr-defined]
602
690
  if not self.allow_migrate_model(schema_editor.connection, model):
603
- return
691
+ return None
604
692
 
605
693
  if self.old_fields:
606
- from_model = from_state.models_registry.get_model(
694
+ from_model = from_state.models_registry.get_model( # type: ignore[attr-defined]
607
695
  package_label, self.model_name
608
696
  )
609
697
  columns = [
610
- from_model._meta.get_field(field).column for field in self.old_fields
698
+ from_model._model_meta.get_field(field).column
699
+ for field in self.old_fields
611
700
  ]
612
701
  matching_index_name = schema_editor._constraint_names(
613
702
  from_model, column_names=columns, index=True
@@ -616,7 +705,7 @@ class RenameIndex(IndexOperation):
616
705
  raise ValueError(
617
706
  "Found wrong number ({}) of indexes for {}({}).".format(
618
707
  len(matching_index_name),
619
- from_model._meta.db_table,
708
+ from_model.model_options.db_table,
620
709
  ", ".join(columns),
621
710
  )
622
711
  )
@@ -626,16 +715,17 @@ class RenameIndex(IndexOperation):
626
715
  )
627
716
  else:
628
717
  from_model_state = from_state.models[package_label, self.model_name_lower]
629
- old_index = from_model_state.get_index_by_name(self.old_name)
718
+ old_index = from_model_state.get_index_by_name(self.old_name) # type: ignore[arg-type]
630
719
  # Don't alter when the index name is not changed.
631
- if old_index.name == self.new_name:
632
- return
720
+ if old_index.name == self.new_name: # type: ignore[attr-defined]
721
+ return None
633
722
 
634
723
  to_model_state = to_state.models[package_label, self.model_name_lower]
635
724
  new_index = to_model_state.get_index_by_name(self.new_name)
636
725
  schema_editor.rename_index(model, old_index, new_index)
726
+ return None
637
727
 
638
- def describe(self):
728
+ def describe(self) -> str:
639
729
  if self.old_name:
640
730
  return (
641
731
  f"Rename index {self.old_name} on {self.model_name} to {self.new_name}"
@@ -646,16 +736,18 @@ class RenameIndex(IndexOperation):
646
736
  )
647
737
 
648
738
  @property
649
- def migration_name_fragment(self):
739
+ def migration_name_fragment(self) -> str:
650
740
  if self.old_name:
651
741
  return f"rename_{self.old_name_lower}_{self.new_name_lower}"
652
742
  return "rename_{}_{}_{}".format(
653
743
  self.model_name_lower,
654
- "_".join(self.old_fields),
744
+ "_".join(self.old_fields), # type: ignore[arg-type]
655
745
  self.new_name_lower,
656
746
  )
657
747
 
658
- def reduce(self, operation, package_label):
748
+ def reduce(
749
+ self, operation: Operation, package_label: str
750
+ ) -> bool | list[Operation]:
659
751
  if (
660
752
  isinstance(operation, RenameIndex)
661
753
  and self.model_name_lower == operation.model_name_lower
@@ -676,19 +768,25 @@ class RenameIndex(IndexOperation):
676
768
  class AddConstraint(IndexOperation):
677
769
  option_name = "constraints"
678
770
 
679
- def __init__(self, model_name, constraint):
771
+ def __init__(self, model_name: str, constraint: Any) -> None:
680
772
  self.model_name = model_name
681
773
  self.constraint = constraint
682
774
 
683
- def state_forwards(self, package_label, state):
775
+ def state_forwards(self, package_label: str, state: ProjectState) -> None:
684
776
  state.add_constraint(package_label, self.model_name_lower, self.constraint)
685
777
 
686
- def database_forwards(self, package_label, schema_editor, from_state, to_state):
687
- model = to_state.models_registry.get_model(package_label, self.model_name)
778
+ def database_forwards(
779
+ self,
780
+ package_label: str,
781
+ schema_editor: BaseDatabaseSchemaEditor,
782
+ from_state: ProjectState,
783
+ to_state: ProjectState,
784
+ ) -> None:
785
+ model = to_state.models_registry.get_model(package_label, self.model_name) # type: ignore[attr-defined]
688
786
  if self.allow_migrate_model(schema_editor.connection, model):
689
787
  schema_editor.add_constraint(model, self.constraint)
690
788
 
691
- def deconstruct(self):
789
+ def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]:
692
790
  return (
693
791
  self.__class__.__name__,
694
792
  [],
@@ -698,32 +796,38 @@ class AddConstraint(IndexOperation):
698
796
  },
699
797
  )
700
798
 
701
- def describe(self):
702
- return f"Create constraint {self.constraint.name} on model {self.model_name}"
799
+ def describe(self) -> str:
800
+ return f"Create constraint {self.constraint.name} on model {self.model_name}" # type: ignore[attr-defined]
703
801
 
704
802
  @property
705
- def migration_name_fragment(self):
706
- return f"{self.model_name_lower}_{self.constraint.name.lower()}"
803
+ def migration_name_fragment(self) -> str:
804
+ return f"{self.model_name_lower}_{self.constraint.name.lower()}" # type: ignore[attr-defined]
707
805
 
708
806
 
709
807
  class RemoveConstraint(IndexOperation):
710
808
  option_name = "constraints"
711
809
 
712
- def __init__(self, model_name, name):
810
+ def __init__(self, model_name: str, name: str) -> None:
713
811
  self.model_name = model_name
714
812
  self.name = name
715
813
 
716
- def state_forwards(self, package_label, state):
814
+ def state_forwards(self, package_label: str, state: ProjectState) -> None:
717
815
  state.remove_constraint(package_label, self.model_name_lower, self.name)
718
816
 
719
- def database_forwards(self, package_label, schema_editor, from_state, to_state):
720
- model = to_state.models_registry.get_model(package_label, self.model_name)
817
+ def database_forwards(
818
+ self,
819
+ package_label: str,
820
+ schema_editor: BaseDatabaseSchemaEditor,
821
+ from_state: ProjectState,
822
+ to_state: ProjectState,
823
+ ) -> None:
824
+ model = to_state.models_registry.get_model(package_label, self.model_name) # type: ignore[attr-defined]
721
825
  if self.allow_migrate_model(schema_editor.connection, model):
722
826
  from_model_state = from_state.models[package_label, self.model_name_lower]
723
827
  constraint = from_model_state.get_constraint_by_name(self.name)
724
828
  schema_editor.remove_constraint(model, constraint)
725
829
 
726
- def deconstruct(self):
830
+ def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]:
727
831
  return (
728
832
  self.__class__.__name__,
729
833
  [],
@@ -733,9 +837,9 @@ class RemoveConstraint(IndexOperation):
733
837
  },
734
838
  )
735
839
 
736
- def describe(self):
840
+ def describe(self) -> str:
737
841
  return f"Remove constraint {self.name} from model {self.model_name}"
738
842
 
739
843
  @property
740
- def migration_name_fragment(self):
844
+ def migration_name_fragment(self) -> str:
741
845
  return f"remove_{self.model_name_lower}_{self.name.lower()}"