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
plain/models/base.py CHANGED
@@ -1,7 +1,14 @@
1
+ from __future__ import annotations
2
+
1
3
  import copy
2
- import inspect
3
4
  import warnings
5
+ from collections.abc import Iterable, Iterator, Sequence
4
6
  from itertools import chain
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ if TYPE_CHECKING:
10
+ from plain.models.meta import Meta
11
+ from plain.models.options import Options
5
12
 
6
13
  import plain.runtime
7
14
  from plain.exceptions import NON_FIELD_ERRORS, ValidationError
@@ -22,19 +29,19 @@ from plain.models.exceptions import (
22
29
  from plain.models.expressions import RawSQL, Value
23
30
  from plain.models.fields import NOT_PROVIDED, PrimaryKeyField
24
31
  from plain.models.fields.reverse_related import ForeignObjectRel
32
+ from plain.models.meta import Meta
25
33
  from plain.models.options import Options
26
34
  from plain.models.query import F, Q, QuerySet
27
- from plain.packages import packages_registry
28
35
  from plain.preflight import PreflightResult
29
36
  from plain.utils.encoding import force_str
30
37
  from plain.utils.hashable import make_hashable
31
38
 
32
39
 
33
40
  class Deferred:
34
- def __repr__(self):
41
+ def __repr__(self) -> str:
35
42
  return "<Deferred field>"
36
43
 
37
- def __str__(self):
44
+ def __str__(self) -> str:
38
45
  return "<Deferred field>"
39
46
 
40
47
 
@@ -44,7 +51,9 @@ DEFERRED = Deferred()
44
51
  class ModelBase(type):
45
52
  """Metaclass for all models."""
46
53
 
47
- def __new__(cls, name, bases, attrs, **kwargs):
54
+ def __new__(
55
+ cls, name: str, bases: tuple[type, ...], attrs: dict[str, Any], **kwargs: Any
56
+ ) -> type:
48
57
  # Don't do any of this for the root models.Model class.
49
58
  if not bases:
50
59
  return super().__new__(cls, name, bases, attrs)
@@ -55,75 +64,14 @@ class ModelBase(type):
55
64
  raise TypeError(
56
65
  f"A model can't extend another model: {name} extends {base}"
57
66
  )
58
- # Meta has to be defined on the model itself.
59
- if hasattr(base, "Meta"):
60
- raise TypeError(
61
- "Meta can only be defined on a model itself, not a parent class: "
62
- f"{name} extends {base}"
63
- )
64
-
65
- new_class = super().__new__(cls, name, bases, attrs, **kwargs)
66
-
67
- new_class._setup_meta()
68
-
69
- # Now go back over all the attrs on this class see if they have a contribute_to_class() method.
70
- # Attributes with contribute_to_class are fields and meta options.
71
- for attr_name, attr_value in inspect.getmembers(new_class):
72
- if attr_name.startswith("_"):
73
- continue
74
-
75
- if not inspect.isclass(attr_value) and hasattr(
76
- attr_value, "contribute_to_class"
77
- ):
78
- if attr_name not in attrs:
79
- # If the field came from an inherited class/mixin,
80
- # we need to make a copy of it to avoid altering the
81
- # original class and other classes that inherit from it.
82
- field = copy.deepcopy(attr_value)
83
- else:
84
- field = attr_value
85
- field.contribute_to_class(new_class, attr_name)
86
-
87
- # Set the name of _meta.indexes. This can't be done in
88
- # Options.contribute_to_class() because fields haven't been added to
89
- # the model at that point.
90
- for index in new_class._meta.indexes:
91
- if not index.name:
92
- index.set_name_with_model(new_class)
93
-
94
- return new_class
95
-
96
- def _setup_meta(cls):
97
- name = cls.__name__
98
- module = cls.__module__
99
-
100
- # The model's Meta class, if it has one.
101
- meta = getattr(cls, "Meta", None)
102
-
103
- # Look for an application configuration to attach the model to.
104
- package_config = packages_registry.get_containing_package_config(module)
105
-
106
- package_label = getattr(meta, "package_label", None)
107
- if package_label is None:
108
- if package_config is None:
109
- raise RuntimeError(
110
- f"Model class {module}.{name} doesn't declare an explicit "
111
- "package_label and isn't in an application in "
112
- "INSTALLED_PACKAGES."
113
- )
114
- else:
115
- package_label = package_config.package_label
116
-
117
- Options(meta, package_label).contribute_to_class(cls, "_meta")
118
67
 
119
- @property
120
- def query(cls) -> QuerySet:
121
- """Create a new QuerySet for this model."""
122
- return cls._meta.queryset
68
+ return super().__new__(cls, name, bases, attrs, **kwargs)
123
69
 
124
70
 
125
71
  class ModelStateFieldsCacheDescriptor:
126
- def __get__(self, instance, cls=None):
72
+ def __get__(
73
+ self, instance: ModelState | None, cls: type | None = None
74
+ ) -> ModelStateFieldsCacheDescriptor | dict[str, Any]:
127
75
  if instance is None:
128
76
  return self
129
77
  res = instance.fields_cache = {}
@@ -142,19 +90,20 @@ class ModelState:
142
90
 
143
91
 
144
92
  class Model(metaclass=ModelBase):
145
- _meta: Options
93
+ # Every model gets an automatic id field
94
+ id = PrimaryKeyField()
146
95
 
147
- # Use descriptors for exception classes instead of metaclass generation
96
+ # Descriptors for other model behavior
97
+ query = QuerySet()
98
+ model_options = Options()
99
+ _model_meta = Meta()
148
100
  DoesNotExist = DoesNotExistDescriptor()
149
101
  MultipleObjectsReturned = MultipleObjectsReturnedDescriptor()
150
102
 
151
- # Every model gets an automatic id field
152
- id = PrimaryKeyField()
153
-
154
- def __init__(self, *args, **kwargs):
103
+ def __init__(self, *args: Any, **kwargs: Any):
155
104
  # Alias some things as locals to avoid repeat global lookups
156
105
  cls = self.__class__
157
- opts = self._meta
106
+ meta = cls._model_meta
158
107
  _setattr = setattr
159
108
  _DEFERRED = DEFERRED
160
109
 
@@ -165,12 +114,12 @@ class Model(metaclass=ModelBase):
165
114
  # overrides it. It should be one or the other; don't duplicate the work
166
115
  # The reason for the kwargs check is that standard iterator passes in by
167
116
  # args, and instantiation for iteration is 33% faster.
168
- if len(args) > len(opts.concrete_fields):
117
+ if len(args) > len(meta.concrete_fields):
169
118
  # Daft, but matches old exception sans the err msg.
170
119
  raise IndexError("Number of args exceeds number of fields")
171
120
 
172
121
  if not kwargs:
173
- fields_iter = iter(opts.concrete_fields)
122
+ fields_iter = iter(meta.concrete_fields)
174
123
  # The ordering of the zip calls matter - zip throws StopIteration
175
124
  # when an iter throws it. So if the first iter throws it, the second
176
125
  # is *not* consumed. We rely on this, so don't change the order
@@ -181,7 +130,7 @@ class Model(metaclass=ModelBase):
181
130
  _setattr(self, field.attname, val)
182
131
  else:
183
132
  # Slower, kwargs-ready version.
184
- fields_iter = iter(opts.fields)
133
+ fields_iter = iter(meta.fields)
185
134
  for val, field in zip(args, fields_iter):
186
135
  if val is _DEFERRED:
187
136
  continue
@@ -236,7 +185,7 @@ class Model(metaclass=ModelBase):
236
185
  _setattr(self, field.attname, val)
237
186
 
238
187
  if kwargs:
239
- property_names = opts._property_names
188
+ property_names = meta._property_names
240
189
  unexpected = ()
241
190
  for prop, value in kwargs.items():
242
191
  # Any remaining kwargs must correspond to properties or virtual
@@ -246,7 +195,7 @@ class Model(metaclass=ModelBase):
246
195
  _setattr(self, prop, value)
247
196
  else:
248
197
  try:
249
- opts.get_field(prop)
198
+ meta.get_field(prop)
250
199
  except FieldDoesNotExist:
251
200
  unexpected += (prop,)
252
201
  else:
@@ -261,24 +210,24 @@ class Model(metaclass=ModelBase):
261
210
  super().__init__()
262
211
 
263
212
  @classmethod
264
- def from_db(cls, field_names, values):
265
- if len(values) != len(cls._meta.concrete_fields):
213
+ def from_db(cls, field_names: Iterable[str], values: Sequence[Any]) -> Model:
214
+ if len(values) != len(cls._model_meta.concrete_fields):
266
215
  values_iter = iter(values)
267
216
  values = [
268
217
  next(values_iter) if f.attname in field_names else DEFERRED
269
- for f in cls._meta.concrete_fields
218
+ for f in cls._model_meta.concrete_fields
270
219
  ]
271
220
  new = cls(*values)
272
221
  new._state.adding = False
273
222
  return new
274
223
 
275
- def __repr__(self):
224
+ def __repr__(self) -> str:
276
225
  return f"<{self.__class__.__name__}: {self}>"
277
226
 
278
- def __str__(self):
227
+ def __str__(self) -> str:
279
228
  return f"{self.__class__.__name__} object ({self.id})"
280
229
 
281
- def __eq__(self, other):
230
+ def __eq__(self, other: object) -> bool:
282
231
  if not isinstance(other, Model):
283
232
  return NotImplemented
284
233
  if self.__class__ != other.__class__:
@@ -288,18 +237,21 @@ class Model(metaclass=ModelBase):
288
237
  return self is other
289
238
  return my_id == other.id
290
239
 
291
- def __hash__(self):
240
+ def __hash__(self) -> int:
292
241
  if self.id is None:
293
242
  raise TypeError("Model instances without primary key value are unhashable")
294
243
  return hash(self.id)
295
244
 
296
- def __reduce__(self):
245
+ def __reduce__(self) -> tuple[Any, tuple[Any, ...], dict[str, Any]]:
297
246
  data = self.__getstate__()
298
247
  data[PLAIN_VERSION_PICKLE_KEY] = plain.runtime.__version__
299
- class_id = self._meta.package_label, self._meta.object_name
248
+ class_id = (
249
+ self.model_options.package_label,
250
+ self.model_options.object_name,
251
+ )
300
252
  return model_unpickle, (class_id,), data
301
253
 
302
- def __getstate__(self):
254
+ def __getstate__(self) -> dict[str, Any]:
303
255
  """Hook to allow choosing the attributes to pickle."""
304
256
  state = self.__dict__.copy()
305
257
  state["_state"] = copy.copy(state["_state"])
@@ -316,7 +268,7 @@ class Model(metaclass=ModelBase):
316
268
  state.pop(attr)
317
269
  return state
318
270
 
319
- def __setstate__(self, state):
271
+ def __setstate__(self, state: dict[str, Any]) -> None:
320
272
  pickled_version = state.get(PLAIN_VERSION_PICKLE_KEY)
321
273
  if pickled_version:
322
274
  if pickled_version != plain.runtime.__version__:
@@ -337,17 +289,17 @@ class Model(metaclass=ModelBase):
337
289
  state[attr] = memoryview(value)
338
290
  self.__dict__.update(state)
339
291
 
340
- def get_deferred_fields(self):
292
+ def get_deferred_fields(self) -> set[str]:
341
293
  """
342
294
  Return a set containing names of deferred fields for this instance.
343
295
  """
344
296
  return {
345
297
  f.attname
346
- for f in self._meta.concrete_fields
298
+ for f in self._model_meta.concrete_fields
347
299
  if f.attname not in self.__dict__
348
300
  }
349
301
 
350
- def refresh_from_db(self, fields=None):
302
+ def refresh_from_db(self, fields: list[str] | None = None) -> None:
351
303
  """
352
304
  Reload field values from the database.
353
305
 
@@ -368,7 +320,7 @@ class Model(metaclass=ModelBase):
368
320
  prefetched_objects_cache = getattr(self, "_prefetched_objects_cache", ())
369
321
  for field in fields:
370
322
  if field in prefetched_objects_cache:
371
- del prefetched_objects_cache[field]
323
+ del prefetched_objects_cache[field] # type: ignore[misc]
372
324
  fields.remove(field)
373
325
  if not fields:
374
326
  return
@@ -378,7 +330,7 @@ class Model(metaclass=ModelBase):
378
330
  "are not allowed in fields."
379
331
  )
380
332
 
381
- db_instance_qs = self.__class__._meta.base_queryset.filter(id=self.id)
333
+ db_instance_qs = self._model_meta.base_queryset.filter(id=self.id)
382
334
 
383
335
  # Use provided fields, if not set then reload all non-deferred fields.
384
336
  deferred_fields = self.get_deferred_fields()
@@ -388,14 +340,14 @@ class Model(metaclass=ModelBase):
388
340
  elif deferred_fields:
389
341
  fields = [
390
342
  f.attname
391
- for f in self._meta.concrete_fields
343
+ for f in self._model_meta.concrete_fields
392
344
  if f.attname not in deferred_fields
393
345
  ]
394
346
  db_instance_qs = db_instance_qs.only(*fields)
395
347
 
396
348
  db_instance = db_instance_qs.get()
397
349
  non_loaded_fields = db_instance.get_deferred_fields()
398
- for field in self._meta.concrete_fields:
350
+ for field in self._model_meta.concrete_fields:
399
351
  if field.attname in non_loaded_fields:
400
352
  # This field wasn't refreshed - skip ahead.
401
353
  continue
@@ -405,11 +357,11 @@ class Model(metaclass=ModelBase):
405
357
  field.delete_cached_value(self)
406
358
 
407
359
  # Clear cached relations.
408
- for field in self._meta.related_objects:
360
+ for field in self._model_meta.related_objects:
409
361
  if field.is_cached(self):
410
362
  field.delete_cached_value(self)
411
363
 
412
- def serializable_value(self, field_name):
364
+ def serializable_value(self, field_name: str) -> Any:
413
365
  """
414
366
  Return the value of the field name for this instance. If the field is
415
367
  a foreign key, return the id value instead of the object. If there's
@@ -421,7 +373,7 @@ class Model(metaclass=ModelBase):
421
373
  and not use this method.
422
374
  """
423
375
  try:
424
- field = self._meta.get_field(field_name)
376
+ field = self._model_meta.get_field(field_name)
425
377
  except FieldDoesNotExist:
426
378
  return getattr(self, field_name)
427
379
  return getattr(self, field.attname)
@@ -429,11 +381,11 @@ class Model(metaclass=ModelBase):
429
381
  def save(
430
382
  self,
431
383
  *,
432
- clean_and_validate=True,
433
- force_insert=False,
434
- force_update=False,
435
- update_fields=None,
436
- ):
384
+ clean_and_validate: bool = True,
385
+ force_insert: bool = False,
386
+ force_update: bool = False,
387
+ update_fields: Iterable[str] | None = None,
388
+ ) -> None:
437
389
  """
438
390
  Save the current instance. Override this in a subclass if you want to
439
391
  control the saving process.
@@ -456,7 +408,7 @@ class Model(metaclass=ModelBase):
456
408
  return
457
409
 
458
410
  update_fields = frozenset(update_fields)
459
- field_names = self._meta._non_pk_concrete_field_names
411
+ field_names = self._model_meta._non_pk_concrete_field_names
460
412
  non_model_fields = update_fields.difference(field_names)
461
413
 
462
414
  if non_model_fields:
@@ -471,7 +423,7 @@ class Model(metaclass=ModelBase):
471
423
  # on the loaded fields.
472
424
  elif not force_insert and deferred_fields:
473
425
  field_names = set()
474
- for field in self._meta.concrete_fields:
426
+ for field in self._model_meta.concrete_fields:
475
427
  if not field.primary_key and not hasattr(field, "through"):
476
428
  field_names.add(field.attname)
477
429
  loaded_fields = field_names.difference(deferred_fields)
@@ -490,11 +442,11 @@ class Model(metaclass=ModelBase):
490
442
  def save_base(
491
443
  self,
492
444
  *,
493
- raw=False,
494
- force_insert=False,
495
- force_update=False,
496
- update_fields=None,
497
- ):
445
+ raw: bool = False,
446
+ force_insert: bool = False,
447
+ force_update: bool = False,
448
+ update_fields: Iterable[str] | None = None,
449
+ ) -> None:
498
450
  """
499
451
  Handle the parts of saving which should be done only once per save,
500
452
  yet need to be done in raw saves, too. This includes some sanity
@@ -510,28 +462,29 @@ class Model(metaclass=ModelBase):
510
462
 
511
463
  with transaction.mark_for_rollback_on_error():
512
464
  self._save_table(
513
- raw,
514
- cls,
515
- force_insert,
516
- force_update,
517
- update_fields,
465
+ raw=raw,
466
+ cls=cls,
467
+ force_insert=force_insert,
468
+ force_update=force_update,
469
+ update_fields=update_fields,
518
470
  )
519
471
  # Once saved, this is no longer a to-be-added instance.
520
472
  self._state.adding = False
521
473
 
522
474
  def _save_table(
523
475
  self,
524
- raw=False,
525
- cls=None,
526
- force_insert=False,
527
- force_update=False,
528
- update_fields=None,
529
- ):
476
+ *,
477
+ raw: bool,
478
+ cls: type[Model],
479
+ force_insert: bool = False,
480
+ force_update: bool = False,
481
+ update_fields: Iterable[str] | None = None,
482
+ ) -> bool:
530
483
  """
531
484
  Do the heavy-lifting involved in saving. Update or insert the data
532
485
  for a single table.
533
486
  """
534
- meta = cls._meta
487
+ meta = cls._model_meta
535
488
  non_pks = [f for f in meta.local_concrete_fields if not f.primary_key]
536
489
 
537
490
  if update_fields:
@@ -591,7 +544,14 @@ class Model(metaclass=ModelBase):
591
544
  setattr(self, field.attname, value)
592
545
  return updated
593
546
 
594
- def _do_update(self, base_qs, id_val, values, update_fields, forced_update):
547
+ def _do_update(
548
+ self,
549
+ base_qs: QuerySet,
550
+ id_val: Any,
551
+ values: list[tuple[Any, Any, Any]],
552
+ update_fields: Iterable[str] | None,
553
+ forced_update: bool,
554
+ ) -> bool:
595
555
  """
596
556
  Try to update the model. Return True if the model was updated (if an
597
557
  update query was done and a matching row was found in the DB).
@@ -606,22 +566,30 @@ class Model(metaclass=ModelBase):
606
566
  return update_fields is not None or filtered.exists()
607
567
  return filtered._update(values) > 0
608
568
 
609
- def _do_insert(self, manager, fields, returning_fields, raw):
569
+ def _do_insert(
570
+ self,
571
+ manager: QuerySet,
572
+ fields: Sequence[Any],
573
+ returning_fields: Sequence[Any],
574
+ raw: bool,
575
+ ) -> list[Any]:
610
576
  """
611
577
  Do an INSERT. If returning_fields is defined then this method should
612
578
  return the newly created data for the model.
613
579
  """
614
- return manager._insert(
580
+ return manager._insert( # type: ignore[return-value, arg-type]
615
581
  [self],
616
- fields=fields,
617
- returning_fields=returning_fields,
582
+ fields=fields, # type: ignore[arg-type]
583
+ returning_fields=returning_fields, # type: ignore[arg-type]
618
584
  raw=raw,
619
585
  )
620
586
 
621
- def _prepare_related_fields_for_save(self, operation_name, fields=None):
587
+ def _prepare_related_fields_for_save(
588
+ self, operation_name: str, fields: Sequence[Any] | None = None
589
+ ) -> None:
622
590
  # Ensure that a model instance without a PK hasn't been assigned to
623
591
  # a ForeignKey on this model. If the field is nullable, allowing the save would result in silent data loss.
624
- for field in self._meta.concrete_fields:
592
+ for field in self._model_meta.concrete_fields:
625
593
  if fields and field not in fields:
626
594
  continue
627
595
  # If the related field isn't cached, then an instance hasn't been
@@ -655,10 +623,10 @@ class Model(metaclass=ModelBase):
655
623
  ):
656
624
  field.delete_cached_value(self)
657
625
 
658
- def delete(self):
626
+ def delete(self) -> tuple[int, dict[str, int]]:
659
627
  if self.id is None:
660
628
  raise ValueError(
661
- f"{self._meta.object_name} object can't be deleted because its id attribute is set "
629
+ f"{self.model_options.object_name} object can't be deleted because its id attribute is set "
662
630
  "to None."
663
631
  )
664
632
  collector = Collector(origin=self)
@@ -668,7 +636,7 @@ class Model(metaclass=ModelBase):
668
636
  def get_field_display(self, field_name: str) -> str:
669
637
  """Get the display value for a field, especially useful for fields with choices."""
670
638
  # Get the field object from the field name
671
- field = self._meta.get_field(field_name)
639
+ field = self._model_meta.get_field(field_name)
672
640
  value = getattr(self, field.attname)
673
641
 
674
642
  # If field has no choices, just return the value as string
@@ -681,24 +649,26 @@ class Model(metaclass=ModelBase):
681
649
  choices_dict.get(make_hashable(value), value), strings_only=True
682
650
  )
683
651
 
684
- def _get_field_value_map(self, meta, exclude=None):
652
+ def _get_field_value_map(
653
+ self, meta: Meta | None, exclude: set[str] | None = None
654
+ ) -> dict[str, Value]:
685
655
  if exclude is None:
686
656
  exclude = set()
687
- meta = meta or self._meta
657
+ meta = meta or self._model_meta
688
658
  return {
689
659
  field.name: Value(getattr(self, field.attname), field)
690
660
  for field in meta.local_concrete_fields
691
661
  if field.name not in exclude
692
662
  }
693
663
 
694
- def prepare_database_save(self, field):
664
+ def prepare_database_save(self, field: Any) -> Any:
695
665
  if self.id is None:
696
666
  raise ValueError(
697
667
  f"Unsaved model instance {self!r} cannot be used in an ORM query."
698
668
  )
699
669
  return getattr(self, field.remote_field.get_related_field().attname)
700
670
 
701
- def clean(self):
671
+ def clean(self) -> None:
702
672
  """
703
673
  Hook for doing any extra model-wide validation after clean() has been
704
674
  called on every field by self.clean_fields. Any ValidationError raised
@@ -707,7 +677,7 @@ class Model(metaclass=ModelBase):
707
677
  """
708
678
  pass
709
679
 
710
- def validate_unique(self, exclude=None):
680
+ def validate_unique(self, exclude: set[str] | None = None) -> None:
711
681
  """
712
682
  Check unique constraints on the model and raise ValidationError if any
713
683
  failed.
@@ -717,7 +687,9 @@ class Model(metaclass=ModelBase):
717
687
  if errors := self._perform_unique_checks(unique_checks):
718
688
  raise ValidationError(errors)
719
689
 
720
- def _get_unique_checks(self, exclude=None):
690
+ def _get_unique_checks(
691
+ self, exclude: set[str] | None = None
692
+ ) -> list[tuple[type, tuple[str, ...]]]:
721
693
  """
722
694
  Return a list of checks to perform. Since validate_unique() could be
723
695
  called from a ModelForm, some fields may have been excluded; we can't
@@ -732,7 +704,7 @@ class Model(metaclass=ModelBase):
732
704
  # Gather a list of checks for fields declared as unique and add them to
733
705
  # the list of checks.
734
706
 
735
- fields_with_class = [(self.__class__, self._meta.local_fields)]
707
+ fields_with_class = [(self.__class__, self._model_meta.local_fields)]
736
708
 
737
709
  for model_class, fields in fields_with_class:
738
710
  for f in fields:
@@ -744,7 +716,9 @@ class Model(metaclass=ModelBase):
744
716
 
745
717
  return unique_checks
746
718
 
747
- def _perform_unique_checks(self, unique_checks):
719
+ def _perform_unique_checks(
720
+ self, unique_checks: list[tuple[type, tuple[str, ...]]]
721
+ ) -> dict[str, list[ValidationError]]:
748
722
  errors = {}
749
723
 
750
724
  for model_class, unique_check in unique_checks:
@@ -753,7 +727,7 @@ class Model(metaclass=ModelBase):
753
727
 
754
728
  lookup_kwargs = {}
755
729
  for field_name in unique_check:
756
- f = self._meta.get_field(field_name)
730
+ f = self._model_meta.get_field(field_name)
757
731
  lookup_value = getattr(self, f.attname)
758
732
  # TODO: Handle multiple backends with different feature flags.
759
733
  if lookup_value is None:
@@ -768,7 +742,7 @@ class Model(metaclass=ModelBase):
768
742
  if len(unique_check) != len(lookup_kwargs):
769
743
  continue
770
744
 
771
- qs = model_class.query.filter(**lookup_kwargs)
745
+ qs = model_class.query.filter(**lookup_kwargs) # type: ignore[attr-defined]
772
746
 
773
747
  # Exclude the current object from the query if we are editing an
774
748
  # instance (as opposed to creating a new one)
@@ -788,18 +762,20 @@ class Model(metaclass=ModelBase):
788
762
 
789
763
  return errors
790
764
 
791
- def unique_error_message(self, model_class, unique_check):
792
- opts = model_class._meta
765
+ def unique_error_message(
766
+ self, model_class: type[Model], unique_check: tuple[str, ...]
767
+ ) -> ValidationError:
768
+ meta = model_class._model_meta
793
769
 
794
770
  params = {
795
771
  "model": self,
796
772
  "model_class": model_class,
797
- "model_name": opts.model_name,
773
+ "model_name": model_class.model_options.model_name,
798
774
  "unique_check": unique_check,
799
775
  }
800
776
 
801
777
  if len(unique_check) == 1:
802
- field = opts.get_field(unique_check[0])
778
+ field = meta.get_field(unique_check[0])
803
779
  params["field_label"] = field.name
804
780
  return ValidationError(
805
781
  message=field.error_messages["unique"],
@@ -807,7 +783,7 @@ class Model(metaclass=ModelBase):
807
783
  params=params,
808
784
  )
809
785
  else:
810
- field_names = [opts.get_field(f).name for f in unique_check]
786
+ field_names = [meta.get_field(f).name for f in unique_check]
811
787
 
812
788
  # Put an "and" before the last one
813
789
  field_names[-1] = f"and {field_names[-1]}"
@@ -820,7 +796,7 @@ class Model(metaclass=ModelBase):
820
796
  params["field_label"] = " ".join(field_names)
821
797
 
822
798
  # Use the first field as the message format...
823
- message = opts.get_field(unique_check[0]).error_messages["unique"]
799
+ message = meta.get_field(unique_check[0]).error_messages["unique"]
824
800
 
825
801
  return ValidationError(
826
802
  message=message,
@@ -828,11 +804,11 @@ class Model(metaclass=ModelBase):
828
804
  params=params,
829
805
  )
830
806
 
831
- def get_constraints(self):
832
- constraints = [(self.__class__, self._meta.constraints)]
807
+ def get_constraints(self) -> list[tuple[type, list[Any]]]:
808
+ constraints = [(self.__class__, self.model_options.constraints)]
833
809
  return constraints
834
810
 
835
- def validate_constraints(self, exclude=None):
811
+ def validate_constraints(self, exclude: set[str] | None = None) -> None:
836
812
  constraints = self.get_constraints()
837
813
 
838
814
  errors = {}
@@ -852,8 +828,12 @@ class Model(metaclass=ModelBase):
852
828
  raise ValidationError(errors)
853
829
 
854
830
  def full_clean(
855
- self, *, exclude=None, validate_unique=True, validate_constraints=True
856
- ):
831
+ self,
832
+ *,
833
+ exclude: set[str] | Iterable[str] | None = None,
834
+ validate_unique: bool = True,
835
+ validate_constraints: bool = True,
836
+ ) -> None:
857
837
  """
858
838
  Call clean_fields(), clean(), validate_unique(), and
859
839
  validate_constraints() on the model. Raise a ValidationError for any
@@ -900,7 +880,7 @@ class Model(metaclass=ModelBase):
900
880
  if errors:
901
881
  raise ValidationError(errors)
902
882
 
903
- def clean_fields(self, exclude=None):
883
+ def clean_fields(self, exclude: set[str] | None = None) -> None:
904
884
  """
905
885
  Clean all fields and raise a ValidationError containing a dict
906
886
  of all validation errors if any occur.
@@ -909,7 +889,7 @@ class Model(metaclass=ModelBase):
909
889
  exclude = set()
910
890
 
911
891
  errors = {}
912
- for f in self._meta.fields:
892
+ for f in self._model_meta.fields:
913
893
  if f.name in exclude:
914
894
  continue
915
895
  # Skip validation for empty fields with required=False. The developer
@@ -926,7 +906,7 @@ class Model(metaclass=ModelBase):
926
906
  raise ValidationError(errors)
927
907
 
928
908
  @classmethod
929
- def preflight(cls):
909
+ def preflight(cls) -> list[PreflightResult]:
930
910
  errors = []
931
911
 
932
912
  errors += [
@@ -956,13 +936,13 @@ class Model(metaclass=ModelBase):
956
936
  return errors
957
937
 
958
938
  @classmethod
959
- def _check_db_table_comment(cls):
960
- if not cls._meta.db_table_comment:
939
+ def _check_db_table_comment(cls) -> list[PreflightResult]:
940
+ if not cls.model_options.db_table_comment:
961
941
  return []
962
942
  errors = []
963
943
  if not (
964
944
  db_connection.features.supports_comments
965
- or "supports_comments" in cls._meta.required_db_features
945
+ or "supports_comments" in cls.model_options.required_db_features
966
946
  ):
967
947
  errors.append(
968
948
  PreflightResult(
@@ -976,23 +956,23 @@ class Model(metaclass=ModelBase):
976
956
  return errors
977
957
 
978
958
  @classmethod
979
- def _check_fields(cls):
959
+ def _check_fields(cls) -> list[PreflightResult]:
980
960
  """Perform all field checks."""
981
961
  errors = []
982
- for field in cls._meta.local_fields:
962
+ for field in cls._model_meta.local_fields:
983
963
  errors.extend(field.preflight(from_model=cls))
984
- for field in cls._meta.local_many_to_many:
964
+ for field in cls._model_meta.local_many_to_many:
985
965
  errors.extend(field.preflight(from_model=cls))
986
966
  return errors
987
967
 
988
968
  @classmethod
989
- def _check_m2m_through_same_relationship(cls):
969
+ def _check_m2m_through_same_relationship(cls) -> list[PreflightResult]:
990
970
  """Check if no relationship model is used by more than one m2m field."""
991
971
 
992
972
  errors = []
993
973
  seen_intermediary_signatures = []
994
974
 
995
- fields = cls._meta.local_many_to_many
975
+ fields = cls._model_meta.local_many_to_many
996
976
 
997
977
  # Skip when the target model wasn't found.
998
978
  fields = (f for f in fields if isinstance(f.remote_field.model, ModelBase))
@@ -1011,7 +991,7 @@ class Model(metaclass=ModelBase):
1011
991
  errors.append(
1012
992
  PreflightResult(
1013
993
  fix="The model has two identical many-to-many relations "
1014
- f"through the intermediate model '{f.remote_field.through._meta.label}'.",
994
+ f"through the intermediate model '{f.remote_field.through.model_options.label}'.",
1015
995
  obj=cls,
1016
996
  id="models.duplicate_many_to_many_relations",
1017
997
  )
@@ -1021,10 +1001,12 @@ class Model(metaclass=ModelBase):
1021
1001
  return errors
1022
1002
 
1023
1003
  @classmethod
1024
- def _check_id_field(cls):
1004
+ def _check_id_field(cls) -> list[PreflightResult]:
1025
1005
  """Disallow user-defined fields named ``id``."""
1026
1006
  if any(
1027
- f for f in cls._meta.local_fields if f.name == "id" and not f.auto_created
1007
+ f
1008
+ for f in cls._model_meta.local_fields
1009
+ if f.name == "id" and not f.auto_created
1028
1010
  ):
1029
1011
  return [
1030
1012
  PreflightResult(
@@ -1036,12 +1018,12 @@ class Model(metaclass=ModelBase):
1036
1018
  return []
1037
1019
 
1038
1020
  @classmethod
1039
- def _check_field_name_clashes(cls):
1021
+ def _check_field_name_clashes(cls) -> list[PreflightResult]:
1040
1022
  """Forbid field shadowing in multi-table inheritance."""
1041
1023
  errors = []
1042
1024
  used_fields = {} # name or attname -> field
1043
1025
 
1044
- for f in cls._meta.local_fields:
1026
+ for f in cls._model_meta.local_fields:
1045
1027
  clash = used_fields.get(f.name) or used_fields.get(f.attname) or None
1046
1028
  # Note that we may detect clash between user-defined non-unique
1047
1029
  # field "id" and automatically added unique field "id", both
@@ -1054,7 +1036,7 @@ class Model(metaclass=ModelBase):
1054
1036
  errors.append(
1055
1037
  PreflightResult(
1056
1038
  fix=f"The field '{f.name}' clashes with the field '{clash.name}' "
1057
- f"from model '{clash.model._meta}'.",
1039
+ f"from model '{clash.model.model_options}'.",
1058
1040
  obj=f,
1059
1041
  id="models.field_name_clash",
1060
1042
  )
@@ -1065,12 +1047,12 @@ class Model(metaclass=ModelBase):
1065
1047
  return errors
1066
1048
 
1067
1049
  @classmethod
1068
- def _check_column_name_clashes(cls):
1050
+ def _check_column_name_clashes(cls) -> list[PreflightResult]:
1069
1051
  # Store a list of column names which have already been used by other fields.
1070
1052
  used_column_names = []
1071
1053
  errors = []
1072
1054
 
1073
- for f in cls._meta.local_fields:
1055
+ for f in cls._model_meta.local_fields:
1074
1056
  _, column_name = f.get_attname_column()
1075
1057
 
1076
1058
  # Ensure the column name is not already in use.
@@ -1089,7 +1071,7 @@ class Model(metaclass=ModelBase):
1089
1071
  return errors
1090
1072
 
1091
1073
  @classmethod
1092
- def _check_model_name_db_lookup_clashes(cls):
1074
+ def _check_model_name_db_lookup_clashes(cls) -> list[PreflightResult]:
1093
1075
  errors = []
1094
1076
  model_name = cls.__name__
1095
1077
  if model_name.startswith("_") or model_name.endswith("_"):
@@ -1113,12 +1095,14 @@ class Model(metaclass=ModelBase):
1113
1095
  return errors
1114
1096
 
1115
1097
  @classmethod
1116
- def _check_property_name_related_field_accessor_clashes(cls):
1098
+ def _check_property_name_related_field_accessor_clashes(
1099
+ cls,
1100
+ ) -> list[PreflightResult]:
1117
1101
  errors = []
1118
- property_names = cls._meta._property_names
1102
+ property_names = cls._model_meta._property_names
1119
1103
  related_field_accessors = (
1120
1104
  f.get_attname()
1121
- for f in cls._meta._get_fields(reverse=False)
1105
+ for f in cls._model_meta._get_fields(reverse=False)
1122
1106
  if f.is_relation and f.related_model is not None
1123
1107
  )
1124
1108
  for accessor in related_field_accessors:
@@ -1134,9 +1118,9 @@ class Model(metaclass=ModelBase):
1134
1118
  return errors
1135
1119
 
1136
1120
  @classmethod
1137
- def _check_single_primary_key(cls):
1121
+ def _check_single_primary_key(cls) -> list[PreflightResult]:
1138
1122
  errors = []
1139
- if sum(1 for f in cls._meta.local_fields if f.primary_key) > 1:
1123
+ if sum(1 for f in cls._model_meta.local_fields if f.primary_key) > 1:
1140
1124
  errors.append(
1141
1125
  PreflightResult(
1142
1126
  fix="The model cannot have more than one field with "
@@ -1148,11 +1132,11 @@ class Model(metaclass=ModelBase):
1148
1132
  return errors
1149
1133
 
1150
1134
  @classmethod
1151
- def _check_indexes(cls):
1135
+ def _check_indexes(cls) -> list[PreflightResult]:
1152
1136
  """Check fields, names, and conditions of indexes."""
1153
1137
  errors = []
1154
1138
  references = set()
1155
- for index in cls._meta.indexes:
1139
+ for index in cls.model_options.indexes:
1156
1140
  # Index name can't start with an underscore or a number, restricted
1157
1141
  # for cross-database compatibility with Oracle.
1158
1142
  if index.name[0] == "_" or index.name[0].isdigit():
@@ -1180,8 +1164,8 @@ class Model(metaclass=ModelBase):
1180
1164
  )
1181
1165
  if not (
1182
1166
  db_connection.features.supports_partial_indexes
1183
- or "supports_partial_indexes" in cls._meta.required_db_features
1184
- ) and any(index.condition is not None for index in cls._meta.indexes):
1167
+ or "supports_partial_indexes" in cls.model_options.required_db_features
1168
+ ) and any(index.condition is not None for index in cls.model_options.indexes):
1185
1169
  errors.append(
1186
1170
  PreflightResult(
1187
1171
  fix=f"{db_connection.display_name} does not support indexes with conditions. "
@@ -1194,8 +1178,8 @@ class Model(metaclass=ModelBase):
1194
1178
  )
1195
1179
  if not (
1196
1180
  db_connection.features.supports_covering_indexes
1197
- or "supports_covering_indexes" in cls._meta.required_db_features
1198
- ) and any(index.include for index in cls._meta.indexes):
1181
+ or "supports_covering_indexes" in cls.model_options.required_db_features
1182
+ ) and any(index.include for index in cls.model_options.indexes):
1199
1183
  errors.append(
1200
1184
  PreflightResult(
1201
1185
  fix=f"{db_connection.display_name} does not support indexes with non-key columns. "
@@ -1208,8 +1192,8 @@ class Model(metaclass=ModelBase):
1208
1192
  )
1209
1193
  if not (
1210
1194
  db_connection.features.supports_expression_indexes
1211
- or "supports_expression_indexes" in cls._meta.required_db_features
1212
- ) and any(index.contains_expressions for index in cls._meta.indexes):
1195
+ or "supports_expression_indexes" in cls.model_options.required_db_features
1196
+ ) and any(index.contains_expressions for index in cls.model_options.indexes):
1213
1197
  errors.append(
1214
1198
  PreflightResult(
1215
1199
  fix=f"{db_connection.display_name} does not support indexes on expressions. "
@@ -1221,21 +1205,27 @@ class Model(metaclass=ModelBase):
1221
1205
  )
1222
1206
  )
1223
1207
  fields = [
1224
- field for index in cls._meta.indexes for field, _ in index.fields_orders
1208
+ field
1209
+ for index in cls.model_options.indexes
1210
+ for field, _ in index.fields_orders
1211
+ ]
1212
+ fields += [
1213
+ include for index in cls.model_options.indexes for include in index.include
1225
1214
  ]
1226
- fields += [include for index in cls._meta.indexes for include in index.include]
1227
1215
  fields += references
1228
1216
  errors.extend(cls._check_local_fields(fields, "indexes"))
1229
1217
  return errors
1230
1218
 
1231
1219
  @classmethod
1232
- def _check_local_fields(cls, fields, option):
1220
+ def _check_local_fields(
1221
+ cls, fields: Iterable[str], option: str
1222
+ ) -> list[PreflightResult]:
1233
1223
  from plain import models
1234
1224
 
1235
1225
  # In order to avoid hitting the relation tree prematurely, we use our
1236
1226
  # own fields_map instead of using get_field()
1237
1227
  forward_fields_map = {}
1238
- for field in cls._meta._get_fields(reverse=False):
1228
+ for field in cls._model_meta._get_fields(reverse=False):
1239
1229
  forward_fields_map[field.name] = field
1240
1230
  if hasattr(field, "attname"):
1241
1231
  forward_fields_map[field.attname] = field
@@ -1262,11 +1252,11 @@ class Model(metaclass=ModelBase):
1262
1252
  id="models.m2m_field_in_meta_option",
1263
1253
  )
1264
1254
  )
1265
- elif field not in cls._meta.local_fields:
1255
+ elif field not in cls._model_meta.local_fields:
1266
1256
  errors.append(
1267
1257
  PreflightResult(
1268
1258
  fix=f"'{option}' refers to field '{field_name}' which is not local to model "
1269
- f"'{cls._meta.object_name}'. This issue may be caused by multi-table inheritance.",
1259
+ f"'{cls.model_options.object_name}'. This issue may be caused by multi-table inheritance.",
1270
1260
  obj=cls,
1271
1261
  id="models.non_local_field_reference",
1272
1262
  )
@@ -1274,16 +1264,16 @@ class Model(metaclass=ModelBase):
1274
1264
  return errors
1275
1265
 
1276
1266
  @classmethod
1277
- def _check_ordering(cls):
1267
+ def _check_ordering(cls) -> list[PreflightResult]:
1278
1268
  """
1279
1269
  Check "ordering" option -- is it a list of strings and do all fields
1280
1270
  exist?
1281
1271
  """
1282
1272
 
1283
- if not cls._meta.ordering:
1273
+ if not cls.model_options.ordering:
1284
1274
  return []
1285
1275
 
1286
- if not isinstance(cls._meta.ordering, list | tuple):
1276
+ if not isinstance(cls.model_options.ordering, list | tuple):
1287
1277
  return [
1288
1278
  PreflightResult(
1289
1279
  fix="'ordering' must be a tuple or list (even if you want to order by "
@@ -1294,7 +1284,7 @@ class Model(metaclass=ModelBase):
1294
1284
  ]
1295
1285
 
1296
1286
  errors = []
1297
- fields = cls._meta.ordering
1287
+ fields = cls.model_options.ordering
1298
1288
 
1299
1289
  # Skip expressions and '?' fields.
1300
1290
  fields = (f for f in fields if isinstance(f, str) and f != "?")
@@ -1318,9 +1308,9 @@ class Model(metaclass=ModelBase):
1318
1308
  fld = None
1319
1309
  for part in field.split(LOOKUP_SEP):
1320
1310
  try:
1321
- fld = _cls._meta.get_field(part)
1311
+ fld = _cls._model_meta.get_field(part)
1322
1312
  if fld.is_relation:
1323
- _cls = fld.path_infos[-1].to_opts.model
1313
+ _cls = fld.path_infos[-1].to_meta.model
1324
1314
  else:
1325
1315
  _cls = None
1326
1316
  except (FieldDoesNotExist, AttributeError):
@@ -1341,13 +1331,13 @@ class Model(metaclass=ModelBase):
1341
1331
 
1342
1332
  # Any field name that is not present in field_names does not exist.
1343
1333
  # Also, ordering by m2m fields is not allowed.
1344
- opts = cls._meta
1334
+ meta = cls._model_meta
1345
1335
  valid_fields = set(
1346
1336
  chain.from_iterable(
1347
1337
  (f.name, f.attname)
1348
1338
  if not (f.auto_created and not f.concrete)
1349
1339
  else (f.field.related_query_name(),)
1350
- for f in chain(opts.fields, opts.related_objects)
1340
+ for f in chain(meta.fields, meta.related_objects)
1351
1341
  )
1352
1342
  )
1353
1343
 
@@ -1365,7 +1355,7 @@ class Model(metaclass=ModelBase):
1365
1355
  return errors
1366
1356
 
1367
1357
  @classmethod
1368
- def _check_long_column_names(cls):
1358
+ def _check_long_column_names(cls) -> list[PreflightResult]:
1369
1359
  """
1370
1360
  Check that any auto-generated column names are shorter than the limits
1371
1361
  for each database in which the model will be created.
@@ -1380,7 +1370,7 @@ class Model(metaclass=ModelBase):
1380
1370
  if allowed_len is None:
1381
1371
  return errors
1382
1372
 
1383
- for f in cls._meta.local_fields:
1373
+ for f in cls._model_meta.local_fields:
1384
1374
  _, column_name = f.get_attname_column()
1385
1375
 
1386
1376
  # Check if auto-generated name for the field is too long
@@ -1400,14 +1390,14 @@ class Model(metaclass=ModelBase):
1400
1390
  )
1401
1391
  )
1402
1392
 
1403
- for f in cls._meta.local_many_to_many:
1393
+ for f in cls._model_meta.local_many_to_many:
1404
1394
  # Skip nonexistent models.
1405
1395
  if isinstance(f.remote_field.through, str):
1406
1396
  continue
1407
1397
 
1408
1398
  # Check if auto-generated name for the M2M field is too long
1409
1399
  # for the database.
1410
- for m2m in f.remote_field.through._meta.local_fields:
1400
+ for m2m in f.remote_field.through._model_meta.local_fields:
1411
1401
  _, rel_name = m2m.get_attname_column()
1412
1402
  if (
1413
1403
  m2m.db_column is None
@@ -1428,7 +1418,7 @@ class Model(metaclass=ModelBase):
1428
1418
  return errors
1429
1419
 
1430
1420
  @classmethod
1431
- def _get_expr_references(cls, expr):
1421
+ def _get_expr_references(cls, expr: Any) -> Iterator[tuple[str, ...]]:
1432
1422
  if isinstance(expr, Q):
1433
1423
  for child in expr.children:
1434
1424
  if isinstance(child, tuple):
@@ -1444,14 +1434,15 @@ class Model(metaclass=ModelBase):
1444
1434
  yield from cls._get_expr_references(src_expr)
1445
1435
 
1446
1436
  @classmethod
1447
- def _check_constraints(cls):
1437
+ def _check_constraints(cls) -> list[PreflightResult]:
1448
1438
  errors = []
1449
1439
  if not (
1450
1440
  db_connection.features.supports_table_check_constraints
1451
- or "supports_table_check_constraints" in cls._meta.required_db_features
1441
+ or "supports_table_check_constraints"
1442
+ in cls.model_options.required_db_features
1452
1443
  ) and any(
1453
1444
  isinstance(constraint, CheckConstraint)
1454
- for constraint in cls._meta.constraints
1445
+ for constraint in cls.model_options.constraints
1455
1446
  ):
1456
1447
  errors.append(
1457
1448
  PreflightResult(
@@ -1466,11 +1457,11 @@ class Model(metaclass=ModelBase):
1466
1457
 
1467
1458
  if not (
1468
1459
  db_connection.features.supports_partial_indexes
1469
- or "supports_partial_indexes" in cls._meta.required_db_features
1460
+ or "supports_partial_indexes" in cls.model_options.required_db_features
1470
1461
  ) and any(
1471
1462
  isinstance(constraint, UniqueConstraint)
1472
1463
  and constraint.condition is not None
1473
- for constraint in cls._meta.constraints
1464
+ for constraint in cls.model_options.constraints
1474
1465
  ):
1475
1466
  errors.append(
1476
1467
  PreflightResult(
@@ -1486,11 +1477,11 @@ class Model(metaclass=ModelBase):
1486
1477
  if not (
1487
1478
  db_connection.features.supports_deferrable_unique_constraints
1488
1479
  or "supports_deferrable_unique_constraints"
1489
- in cls._meta.required_db_features
1480
+ in cls.model_options.required_db_features
1490
1481
  ) and any(
1491
1482
  isinstance(constraint, UniqueConstraint)
1492
1483
  and constraint.deferrable is not None
1493
- for constraint in cls._meta.constraints
1484
+ for constraint in cls.model_options.constraints
1494
1485
  ):
1495
1486
  errors.append(
1496
1487
  PreflightResult(
@@ -1505,10 +1496,10 @@ class Model(metaclass=ModelBase):
1505
1496
 
1506
1497
  if not (
1507
1498
  db_connection.features.supports_covering_indexes
1508
- or "supports_covering_indexes" in cls._meta.required_db_features
1499
+ or "supports_covering_indexes" in cls.model_options.required_db_features
1509
1500
  ) and any(
1510
1501
  isinstance(constraint, UniqueConstraint) and constraint.include
1511
- for constraint in cls._meta.constraints
1502
+ for constraint in cls.model_options.constraints
1512
1503
  ):
1513
1504
  errors.append(
1514
1505
  PreflightResult(
@@ -1523,10 +1514,10 @@ class Model(metaclass=ModelBase):
1523
1514
 
1524
1515
  if not (
1525
1516
  db_connection.features.supports_expression_indexes
1526
- or "supports_expression_indexes" in cls._meta.required_db_features
1517
+ or "supports_expression_indexes" in cls.model_options.required_db_features
1527
1518
  ) and any(
1528
1519
  isinstance(constraint, UniqueConstraint) and constraint.contains_expressions
1529
- for constraint in cls._meta.constraints
1520
+ for constraint in cls.model_options.constraints
1530
1521
  ):
1531
1522
  errors.append(
1532
1523
  PreflightResult(
@@ -1541,22 +1532,23 @@ class Model(metaclass=ModelBase):
1541
1532
  fields = set(
1542
1533
  chain.from_iterable(
1543
1534
  (*constraint.fields, *constraint.include)
1544
- for constraint in cls._meta.constraints
1535
+ for constraint in cls.model_options.constraints
1545
1536
  if isinstance(constraint, UniqueConstraint)
1546
1537
  )
1547
1538
  )
1548
1539
  references = set()
1549
- for constraint in cls._meta.constraints:
1540
+ for constraint in cls.model_options.constraints:
1550
1541
  if isinstance(constraint, UniqueConstraint):
1551
1542
  if (
1552
1543
  db_connection.features.supports_partial_indexes
1553
- or "supports_partial_indexes" not in cls._meta.required_db_features
1544
+ or "supports_partial_indexes"
1545
+ not in cls.model_options.required_db_features
1554
1546
  ) and isinstance(constraint.condition, Q):
1555
1547
  references.update(cls._get_expr_references(constraint.condition))
1556
1548
  if (
1557
1549
  db_connection.features.supports_expression_indexes
1558
1550
  or "supports_expression_indexes"
1559
- not in cls._meta.required_db_features
1551
+ not in cls.model_options.required_db_features
1560
1552
  ) and constraint.contains_expressions:
1561
1553
  for expression in constraint.expressions:
1562
1554
  references.update(cls._get_expr_references(expression))
@@ -1564,7 +1556,7 @@ class Model(metaclass=ModelBase):
1564
1556
  if (
1565
1557
  db_connection.features.supports_table_check_constraints
1566
1558
  or "supports_table_check_constraints"
1567
- not in cls._meta.required_db_features
1559
+ not in cls.model_options.required_db_features
1568
1560
  ):
1569
1561
  if isinstance(constraint.check, Q):
1570
1562
  references.update(cls._get_expr_references(constraint.check))
@@ -1588,7 +1580,7 @@ class Model(metaclass=ModelBase):
1588
1580
  # If it has no lookups it cannot result in a JOIN.
1589
1581
  continue
1590
1582
  try:
1591
- field = cls._meta.get_field(field_name)
1583
+ field = cls._model_meta.get_field(field_name)
1592
1584
  if not field.is_relation or field.many_to_many or field.one_to_many:
1593
1585
  continue
1594
1586
  except FieldDoesNotExist:
@@ -1617,7 +1609,7 @@ class Model(metaclass=ModelBase):
1617
1609
  ########
1618
1610
 
1619
1611
 
1620
- def model_unpickle(model_id):
1612
+ def model_unpickle(model_id: tuple[str, str] | type[Model]) -> Model:
1621
1613
  """Used to unpickle Model subclasses with deferred fields."""
1622
1614
  if isinstance(model_id, tuple):
1623
1615
  model = models_registry.get_model(*model_id)
@@ -1627,4 +1619,4 @@ def model_unpickle(model_id):
1627
1619
  return model.__new__(model)
1628
1620
 
1629
1621
 
1630
- model_unpickle.__safe_for_unpickle__ = True
1622
+ model_unpickle.__safe_for_unpickle__ = True # type: ignore[attr-defined]