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,8 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import collections
2
4
  import json
3
5
  import re
6
+ from collections.abc import Generator, Iterable
4
7
  from functools import cached_property, partial
5
8
  from itertools import chain
9
+ from typing import TYPE_CHECKING, Any
6
10
 
7
11
  from plain.models.constants import LOOKUP_SEP
8
12
  from plain.models.db import DatabaseError, NotSupportedError
@@ -10,6 +14,7 @@ from plain.models.exceptions import EmptyResultSet, FieldError, FullResultSet
10
14
  from plain.models.expressions import F, OrderBy, RawSQL, Ref, Value
11
15
  from plain.models.functions import Cast, Random
12
16
  from plain.models.lookups import Lookup
17
+ from plain.models.meta import Meta
13
18
  from plain.models.query_utils import select_related_descend
14
19
  from plain.models.sql.constants import (
15
20
  CURSOR,
@@ -25,13 +30,18 @@ from plain.models.transaction import TransactionManagementError
25
30
  from plain.utils.hashable import make_hashable
26
31
  from plain.utils.regex_helper import _lazy_re_compile
27
32
 
33
+ if TYPE_CHECKING:
34
+ from plain.models.backends.base.base import BaseDatabaseWrapper
35
+
28
36
 
29
37
  class PositionRef(Ref):
30
- def __init__(self, ordinal, refs, source):
38
+ def __init__(self, ordinal: int, refs: str, source: Any):
31
39
  self.ordinal = ordinal
32
40
  super().__init__(refs, source)
33
41
 
34
- def as_sql(self, compiler, connection):
42
+ def as_sql(
43
+ self, compiler: SQLCompiler, connection: BaseDatabaseWrapper
44
+ ) -> tuple[str, tuple]:
35
45
  return str(self.ordinal), ()
36
46
 
37
47
 
@@ -42,30 +52,32 @@ class SQLCompiler:
42
52
  re.MULTILINE | re.DOTALL,
43
53
  )
44
54
 
45
- def __init__(self, query, connection, elide_empty=True):
55
+ def __init__(
56
+ self, query: Query, connection: BaseDatabaseWrapper, elide_empty: bool = True
57
+ ):
46
58
  self.query = query
47
59
  self.connection = connection
48
60
  # Some queries, e.g. coalesced aggregation, need to be executed even if
49
61
  # they would return an empty result set.
50
62
  self.elide_empty = elide_empty
51
- self.quote_cache = {"*": "*"}
63
+ self.quote_cache: dict[str, str] = {"*": "*"}
52
64
  # The select, klass_info, and annotations are needed by QuerySet.iterator()
53
65
  # these are set as a side-effect of executing the query. Note that we calculate
54
66
  # separately a list of extra select columns needed for grammatical correctness
55
67
  # of the query, but these columns are not included in self.select.
56
- self.select = None
57
- self.annotation_col_map = None
58
- self.klass_info = None
59
- self._meta_ordering = None
68
+ self.select: list[tuple[Any, tuple[str, tuple], str | None]] | None = None
69
+ self.annotation_col_map: dict[str, int] | None = None
70
+ self.klass_info: dict[str, Any] | None = None
71
+ self._meta_ordering: list[str] | None = None
60
72
 
61
- def __repr__(self):
73
+ def __repr__(self) -> str:
62
74
  return (
63
75
  f"<{self.__class__.__qualname__} "
64
76
  f"model={self.query.model.__qualname__} "
65
77
  f"connection={self.connection!r}>"
66
78
  )
67
79
 
68
- def setup_query(self, with_col_aliases=False):
80
+ def setup_query(self, with_col_aliases: bool = False) -> None:
69
81
  if all(self.query.alias_refcount[a] == 0 for a in self.query.alias_map):
70
82
  self.query.get_initial_alias()
71
83
  self.select, self.klass_info, self.annotation_col_map = self.get_select(
@@ -73,7 +85,9 @@ class SQLCompiler:
73
85
  )
74
86
  self.col_count = len(self.select)
75
87
 
76
- def pre_sql_setup(self, with_col_aliases=False):
88
+ def pre_sql_setup(
89
+ self, with_col_aliases: bool = False
90
+ ) -> tuple[list[Any], list[Any], list[tuple[str, tuple]]]:
77
91
  """
78
92
  Do any necessary class setup immediately prior to producing SQL. This
79
93
  is for things that can't necessarily be done in __init__ because we
@@ -81,7 +95,7 @@ class SQLCompiler:
81
95
  """
82
96
  self.setup_query(with_col_aliases=with_col_aliases)
83
97
  order_by = self.get_order_by()
84
- self.where, self.having, self.qualify = self.query.where.split_having_qualify(
98
+ self.where, self.having, self.qualify = self.query.where.split_having_qualify( # type: ignore[attr-defined]
85
99
  must_group_by=self.query.group_by is not None
86
100
  )
87
101
  extra_select = self.get_extra_select(order_by, self.select)
@@ -89,7 +103,9 @@ class SQLCompiler:
89
103
  group_by = self.get_group_by(self.select + extra_select, order_by)
90
104
  return extra_select, order_by, group_by
91
105
 
92
- def get_group_by(self, select, order_by):
106
+ def get_group_by(
107
+ self, select: list[Any], order_by: list[Any]
108
+ ) -> list[tuple[str, tuple]]:
93
109
  """
94
110
  Return a list of 2-tuples of form (sql, params).
95
111
 
@@ -192,7 +208,7 @@ class SQLCompiler:
192
208
  seen.add((sql, params_hash))
193
209
  return result
194
210
 
195
- def collapse_group_by(self, expressions, having):
211
+ def collapse_group_by(self, expressions: list[Any], having: list[Any]) -> list[Any]:
196
212
  # If the database supports group by functional dependence reduction,
197
213
  # then the expressions can be reduced to the set of selected table
198
214
  # primary keys as all other columns are functionally dependent on them.
@@ -222,7 +238,13 @@ class SQLCompiler:
222
238
  ]
223
239
  return expressions
224
240
 
225
- def get_select(self, with_col_aliases=False):
241
+ def get_select(
242
+ self, with_col_aliases: bool = False
243
+ ) -> tuple[
244
+ list[tuple[Any, tuple[str, tuple], str | None]],
245
+ dict[str, Any] | None,
246
+ dict[str, int],
247
+ ]:
226
248
  """
227
249
  Return three values:
228
250
  - a list of 3-tuples of (expression, (sql, params), alias)
@@ -273,7 +295,7 @@ class SQLCompiler:
273
295
 
274
296
  if self.query.select_related:
275
297
  related_klass_infos = self.get_related_selections(select, select_mask)
276
- klass_info["related_klass_infos"] = related_klass_infos
298
+ klass_info["related_klass_infos"] = related_klass_infos # type: ignore[index]
277
299
 
278
300
  ret = []
279
301
  col_idx = 1
@@ -299,15 +321,19 @@ class SQLCompiler:
299
321
  ret.append((col, (sql, params), alias))
300
322
  return ret, klass_info, annotations
301
323
 
302
- def _order_by_pairs(self):
324
+ def _order_by_pairs(self) -> Generator[tuple[OrderBy, bool], None, None]:
303
325
  if self.query.extra_order_by:
304
326
  ordering = self.query.extra_order_by
305
327
  elif not self.query.default_ordering:
306
328
  ordering = self.query.order_by
307
329
  elif self.query.order_by:
308
330
  ordering = self.query.order_by
309
- elif (meta := self.query.get_meta()) and meta.ordering:
310
- ordering = meta.ordering
331
+ elif (
332
+ self.query.model
333
+ and (options := self.query.model.model_options)
334
+ and options.ordering
335
+ ):
336
+ ordering = options.ordering
311
337
  self._meta_ordering = ordering
312
338
  else:
313
339
  ordering = []
@@ -334,24 +360,24 @@ class SQLCompiler:
334
360
  if not self.query.standard_ordering:
335
361
  field = field.copy()
336
362
  field.reverse_ordering()
337
- select_ref = selected_exprs.get(field.expression)
363
+ select_ref = selected_exprs.get(field.expression) # type: ignore[attr-defined]
338
364
  if select_ref or (
339
- isinstance(field.expression, F)
340
- and (select_ref := selected_exprs.get(field.expression.name))
365
+ isinstance(field.expression, F) # type: ignore[attr-defined]
366
+ and (select_ref := selected_exprs.get(field.expression.name)) # type: ignore[attr-defined]
341
367
  ):
342
368
  # Emulation of NULLS (FIRST|LAST) cannot be combined with
343
369
  # the usage of ordering by position.
344
370
  if (
345
- field.nulls_first is None and field.nulls_last is None
371
+ field.nulls_first is None and field.nulls_last is None # type: ignore[attr-defined]
346
372
  ) or self.connection.features.supports_order_by_nulls_modifier:
347
373
  field = field.copy()
348
- field.expression = select_ref
374
+ field.expression = select_ref # type: ignore[assignment]
349
375
  # Alias collisions are not possible when dealing with
350
376
  # combined queries so fallback to it if emulation of NULLS
351
377
  # handling is required.
352
378
  elif self.query.combinator:
353
379
  field = field.copy()
354
- field.expression = Ref(select_ref.refs, select_ref.source)
380
+ field.expression = Ref(select_ref.refs, select_ref.source) # type: ignore[assignment]
355
381
  yield field, select_ref is not None
356
382
  continue
357
383
  if field == "?": # random
@@ -423,11 +449,11 @@ class SQLCompiler:
423
449
  # '-field1__field2__field', etc.
424
450
  yield from self.find_ordering_name(
425
451
  field,
426
- self.query.get_meta(),
452
+ self.query.get_model_meta(),
427
453
  default_order=default_order,
428
454
  )
429
455
 
430
- def get_order_by(self):
456
+ def get_order_by(self) -> list[tuple[Any, tuple[str, tuple, bool]]]:
431
457
  """
432
458
  Return a list of 2-tuples of the form (expr, (sql, params, is_ref)) for
433
459
  the ORDER BY clause.
@@ -488,7 +514,9 @@ class SQLCompiler:
488
514
  result.append((resolved, (sql, params, is_ref)))
489
515
  return result
490
516
 
491
- def get_extra_select(self, order_by, select):
517
+ def get_extra_select(
518
+ self, order_by: list[Any], select: list[Any]
519
+ ) -> list[tuple[Any, tuple[str, tuple], None]]:
492
520
  extra_select = []
493
521
  if self.query.distinct and not self.query.distinct_fields:
494
522
  select_sql = [t[1] for t in select]
@@ -498,7 +526,7 @@ class SQLCompiler:
498
526
  extra_select.append((expr, (without_ordering, params), None))
499
527
  return extra_select
500
528
 
501
- def quote_name_unless_alias(self, name):
529
+ def quote_name_unless_alias(self, name: str) -> str:
502
530
  """
503
531
  A wrapper around connection.ops.quote_name that doesn't quote aliases
504
532
  for table names. This avoids problems with some SQL dialects that treat
@@ -520,7 +548,7 @@ class SQLCompiler:
520
548
  self.quote_cache[name] = r
521
549
  return r
522
550
 
523
- def compile(self, node):
551
+ def compile(self, node: Any) -> tuple[str, tuple]:
524
552
  vendor_impl = getattr(node, "as_" + self.connection.vendor, None)
525
553
  if vendor_impl:
526
554
  sql, params = vendor_impl(self, self.connection)
@@ -528,7 +556,7 @@ class SQLCompiler:
528
556
  sql, params = node.as_sql(self, self.connection)
529
557
  return sql, params
530
558
 
531
- def get_combinator_sql(self, combinator, all):
559
+ def get_combinator_sql(self, combinator: str, all: bool) -> tuple[list[str], list]:
532
560
  features = self.connection.features
533
561
  compilers = [
534
562
  query.get_compiler(elide_empty=self.elide_empty)
@@ -608,7 +636,7 @@ class SQLCompiler:
608
636
  params.extend(part)
609
637
  return result, params
610
638
 
611
- def get_qualify_sql(self):
639
+ def get_qualify_sql(self) -> tuple[list[str], list]:
612
640
  where_parts = []
613
641
  if self.where:
614
642
  where_parts.append(self.where)
@@ -628,7 +656,7 @@ class SQLCompiler:
628
656
  qual_aliases = set()
629
657
  replacements = {}
630
658
 
631
- def collect_replacements(expressions):
659
+ def collect_replacements(expressions: list[Any]) -> None:
632
660
  while expressions:
633
661
  expr = expressions.pop()
634
662
  if expr in replacements:
@@ -702,7 +730,9 @@ class SQLCompiler:
702
730
  result.extend(["ORDER BY", ", ".join(ordering_sqls)])
703
731
  return result, params
704
732
 
705
- def as_sql(self, with_limits=True, with_col_aliases=False):
733
+ def as_sql(
734
+ self, with_limits: bool = True, with_col_aliases: bool = False
735
+ ) -> tuple[str, tuple]:
706
736
  """
707
737
  Create the SQL for this query. Return the SQL string and list of
708
738
  parameters.
@@ -921,11 +951,16 @@ class SQLCompiler:
921
951
  # Finally do cleanup - get rid of the joins we created above.
922
952
  self.query.reset_refcounts(refcounts_before)
923
953
 
924
- def get_default_columns(self, select_mask, start_alias=None, opts=None):
954
+ def get_default_columns(
955
+ self,
956
+ select_mask: Any,
957
+ start_alias: str | None = None,
958
+ opts: Meta | None = None,
959
+ ) -> list[Any]:
925
960
  """
926
961
  Compute the default columns for selecting every field in the base
927
962
  model. Will sometimes be called to pull in related models (e.g. via
928
- select_related), in which case "opts" and "start_alias" will be given
963
+ select_related), in which case "opts" (Meta) and "start_alias" will be given
929
964
  to provide a starting point for the traversal.
930
965
 
931
966
  Return a list of strings, quoted appropriately for use in SQL
@@ -935,8 +970,7 @@ class SQLCompiler:
935
970
  """
936
971
  result = []
937
972
  if opts is None:
938
- if (opts := self.query.get_meta()) is None:
939
- return result
973
+ opts = self.query.get_model_meta()
940
974
  start_alias = start_alias or self.query.get_initial_alias()
941
975
 
942
976
  for field in opts.concrete_fields:
@@ -952,7 +986,7 @@ class SQLCompiler:
952
986
  result.append(column)
953
987
  return result
954
988
 
955
- def get_distinct(self):
989
+ def get_distinct(self) -> tuple[list[str], list]:
956
990
  """
957
991
  Return a quoted list of fields to use in DISTINCT ON part of the query.
958
992
 
@@ -961,7 +995,7 @@ class SQLCompiler:
961
995
  """
962
996
  result = []
963
997
  params = []
964
- opts = self.query.get_meta()
998
+ opts = self.query.get_model_meta()
965
999
 
966
1000
  for name in self.query.distinct_fields:
967
1001
  parts = name.split(LOOKUP_SEP)
@@ -979,8 +1013,13 @@ class SQLCompiler:
979
1013
  return result, params
980
1014
 
981
1015
  def find_ordering_name(
982
- self, name, opts, alias=None, default_order="ASC", already_seen=None
983
- ):
1016
+ self,
1017
+ name: str,
1018
+ meta: Meta,
1019
+ alias: str | None = None,
1020
+ default_order: str = "ASC",
1021
+ already_seen: set | None = None,
1022
+ ) -> list[tuple[OrderBy, bool]]:
984
1023
  """
985
1024
  Return the table alias (the name might be ambiguous, the alias will
986
1025
  not be) and column name for ordering by the given 'name' parameter.
@@ -995,9 +1034,9 @@ class SQLCompiler:
995
1034
  alias,
996
1035
  joins,
997
1036
  path,
998
- opts,
1037
+ meta,
999
1038
  transform_function,
1000
- ) = self._setup_joins(pieces, opts, alias)
1039
+ ) = self._setup_joins(pieces, meta, alias)
1001
1040
 
1002
1041
  # If we get to this point and the field is a relation to another model,
1003
1042
  # append the default ordering for that model unless it is the
@@ -1005,7 +1044,7 @@ class SQLCompiler:
1005
1044
  # there are transforms to process.
1006
1045
  if (
1007
1046
  field.is_relation
1008
- and opts.ordering
1047
+ and meta.model.model_options.ordering
1009
1048
  and getattr(field, "attname", None) != pieces[-1]
1010
1049
  and not getattr(transform_function, "has_transforms", False)
1011
1050
  ):
@@ -1019,7 +1058,7 @@ class SQLCompiler:
1019
1058
  already_seen.add(join_tuple)
1020
1059
 
1021
1060
  results = []
1022
- for item in opts.ordering:
1061
+ for item in meta.model.model_options.ordering:
1023
1062
  if hasattr(item, "resolve_expression") and not isinstance(
1024
1063
  item, OrderBy
1025
1064
  ):
@@ -1032,7 +1071,7 @@ class SQLCompiler:
1032
1071
  results.extend(
1033
1072
  (expr.prefix_references(f"{name}{LOOKUP_SEP}"), is_ref)
1034
1073
  for expr, is_ref in self.find_ordering_name(
1035
- item, opts, alias, order, already_seen
1074
+ item, meta, alias, order, already_seen
1036
1075
  )
1037
1076
  )
1038
1077
  return results
@@ -1042,7 +1081,9 @@ class SQLCompiler:
1042
1081
  for t in targets
1043
1082
  ]
1044
1083
 
1045
- def _setup_joins(self, pieces, opts, alias):
1084
+ def _setup_joins(
1085
+ self, pieces: list[str], meta: Meta, alias: str | None
1086
+ ) -> tuple[Any, Any, str, list, Any, Meta, Any]:
1046
1087
  """
1047
1088
  Helper method for get_order_by() and get_distinct().
1048
1089
 
@@ -1051,13 +1092,13 @@ class SQLCompiler:
1051
1092
  match. Executing SQL where this is not true is an error.
1052
1093
  """
1053
1094
  alias = alias or self.query.get_initial_alias()
1054
- field, targets, opts, joins, path, transform_function = self.query.setup_joins(
1055
- pieces, opts, alias
1095
+ field, targets, meta, joins, path, transform_function = self.query.setup_joins(
1096
+ pieces, meta, alias
1056
1097
  )
1057
1098
  alias = joins[-1]
1058
- return field, targets, alias, joins, path, opts, transform_function
1099
+ return field, targets, alias, joins, path, meta, transform_function
1059
1100
 
1060
- def get_from_clause(self):
1101
+ def get_from_clause(self) -> tuple[list[str], list]:
1061
1102
  """
1062
1103
  Return a list of strings that are joined together to go after the
1063
1104
  "FROM" part of the query, as well as a list any extra parameters that
@@ -1096,26 +1137,29 @@ class SQLCompiler:
1096
1137
 
1097
1138
  def get_related_selections(
1098
1139
  self,
1099
- select,
1100
- select_mask,
1101
- opts=None,
1102
- root_alias=None,
1103
- cur_depth=1,
1104
- requested=None,
1105
- restricted=None,
1106
- ):
1140
+ select: list[Any],
1141
+ select_mask: Any,
1142
+ opts: Meta | None = None,
1143
+ root_alias: str | None = None,
1144
+ cur_depth: int = 1,
1145
+ requested: dict | None = None,
1146
+ restricted: bool | None = None,
1147
+ ) -> list[dict[str, Any]]:
1107
1148
  """
1108
1149
  Fill in the information needed for a select_related query. The current
1109
1150
  depth is measured as the number of connections away from the root model
1110
1151
  (for example, cur_depth=1 means we are looking at models with direct
1111
1152
  connections to the root model).
1153
+
1154
+ Args:
1155
+ opts: Meta for the model being queried (internal metadata)
1112
1156
  """
1113
1157
 
1114
- def _get_field_choices():
1115
- direct_choices = (f.name for f in opts.fields if f.is_relation)
1158
+ def _get_field_choices() -> chain:
1159
+ direct_choices = (f.name for f in opts.fields if f.is_relation) # type: ignore[union-attr]
1116
1160
  reverse_choices = (
1117
1161
  f.field.related_query_name()
1118
- for f in opts.related_objects
1162
+ for f in opts.related_objects # type: ignore[union-attr]
1119
1163
  if f.field.primary_key
1120
1164
  )
1121
1165
  return chain(
@@ -1128,7 +1172,7 @@ class SQLCompiler:
1128
1172
  return related_klass_infos
1129
1173
 
1130
1174
  if not opts:
1131
- opts = self.query.get_meta()
1175
+ opts = self.query.get_model_meta()
1132
1176
  root_alias = self.query.get_initial_alias()
1133
1177
 
1134
1178
  # Setup for the case when only particular related fields should be
@@ -1139,18 +1183,20 @@ class SQLCompiler:
1139
1183
  if restricted:
1140
1184
  requested = self.query.select_related
1141
1185
 
1142
- def get_related_klass_infos(klass_info, related_klass_infos):
1186
+ def get_related_klass_infos(
1187
+ klass_info: dict, related_klass_infos: list
1188
+ ) -> None:
1143
1189
  klass_info["related_klass_infos"] = related_klass_infos
1144
1190
 
1145
1191
  for f in opts.fields:
1146
1192
  fields_found.add(f.name)
1147
1193
 
1148
1194
  if restricted:
1149
- next = requested.get(f.name, {})
1195
+ next = requested.get(f.name, {}) # type: ignore[union-attr]
1150
1196
  if not f.is_relation:
1151
1197
  # If a non-related field is used like a relation,
1152
1198
  # or if a single non-relational field is given.
1153
- if next or f.name in requested:
1199
+ if next or f.name in requested: # type: ignore[operator]
1154
1200
  raise FieldError(
1155
1201
  "Non-relational field given in select_related: '{}'. "
1156
1202
  "Choices are: {}".format(
@@ -1161,7 +1207,7 @@ class SQLCompiler:
1161
1207
  else:
1162
1208
  next = False
1163
1209
 
1164
- if not select_related_descend(f, restricted, requested, select_mask):
1210
+ if not select_related_descend(f, restricted, requested, select_mask): # type: ignore[arg-type]
1165
1211
  continue
1166
1212
  related_select_mask = select_mask.get(f) or {}
1167
1213
  klass_info = {
@@ -1178,7 +1224,9 @@ class SQLCompiler:
1178
1224
  _, _, _, joins, _, _ = self.query.setup_joins([f.name], opts, root_alias)
1179
1225
  alias = joins[-1]
1180
1226
  columns = self.get_default_columns(
1181
- related_select_mask, start_alias=alias, opts=f.remote_field.model._meta
1227
+ related_select_mask,
1228
+ start_alias=alias,
1229
+ opts=f.remote_field.model._model_meta,
1182
1230
  )
1183
1231
  for col in columns:
1184
1232
  select_fields.append(len(select))
@@ -1187,7 +1235,7 @@ class SQLCompiler:
1187
1235
  next_klass_infos = self.get_related_selections(
1188
1236
  select,
1189
1237
  related_select_mask,
1190
- f.remote_field.model._meta,
1238
+ f.remote_field.model._model_meta,
1191
1239
  alias,
1192
1240
  cur_depth + 1,
1193
1241
  next,
@@ -1203,10 +1251,11 @@ class SQLCompiler:
1203
1251
  ]
1204
1252
  for related_field, model in related_fields:
1205
1253
  related_select_mask = select_mask.get(related_field) or {}
1254
+ # type: ignore[arg-type]
1206
1255
  if not select_related_descend(
1207
1256
  related_field,
1208
1257
  restricted,
1209
- requested,
1258
+ requested, # type: ignore[arg-type]
1210
1259
  related_select_mask,
1211
1260
  reverse=True,
1212
1261
  ):
@@ -1231,17 +1280,17 @@ class SQLCompiler:
1231
1280
  columns = self.get_default_columns(
1232
1281
  related_select_mask,
1233
1282
  start_alias=alias,
1234
- opts=model._meta,
1283
+ opts=model._model_meta,
1235
1284
  )
1236
1285
  for col in columns:
1237
1286
  select_fields.append(len(select))
1238
1287
  select.append((col, None))
1239
1288
  klass_info["select_fields"] = select_fields
1240
- next = requested.get(related_field.related_query_name(), {})
1289
+ next = requested.get(related_field.related_query_name(), {}) # type: ignore[union-attr]
1241
1290
  next_klass_infos = self.get_related_selections(
1242
1291
  select,
1243
1292
  related_select_mask,
1244
- model._meta,
1293
+ model._model_meta,
1245
1294
  alias,
1246
1295
  cur_depth + 1,
1247
1296
  next,
@@ -1249,18 +1298,18 @@ class SQLCompiler:
1249
1298
  )
1250
1299
  get_related_klass_infos(klass_info, next_klass_infos)
1251
1300
 
1252
- def local_setter(final_field, obj, from_obj):
1301
+ def local_setter(final_field: Any, obj: Any, from_obj: Any) -> None:
1253
1302
  # Set a reverse fk object when relation is non-empty.
1254
1303
  if from_obj:
1255
1304
  final_field.remote_field.set_cached_value(from_obj, obj)
1256
1305
 
1257
- def local_setter_noop(obj, from_obj):
1306
+ def local_setter_noop(obj: Any, from_obj: Any) -> None:
1258
1307
  pass
1259
1308
 
1260
- def remote_setter(name, obj, from_obj):
1309
+ def remote_setter(name: str, obj: Any, from_obj: Any) -> None:
1261
1310
  setattr(from_obj, name, obj)
1262
1311
 
1263
- for name in list(requested):
1312
+ for name in list(requested): # type: ignore[arg-type]
1264
1313
  # Filtered relations work only on the topmost level.
1265
1314
  if cur_depth > 1:
1266
1315
  break
@@ -1288,24 +1337,24 @@ class SQLCompiler:
1288
1337
  columns = self.get_default_columns(
1289
1338
  field_select_mask,
1290
1339
  start_alias=alias,
1291
- opts=model._meta,
1340
+ opts=model._model_meta,
1292
1341
  )
1293
1342
  for col in columns:
1294
1343
  select_fields.append(len(select))
1295
1344
  select.append((col, None))
1296
1345
  klass_info["select_fields"] = select_fields
1297
- next_requested = requested.get(name, {})
1346
+ next_requested = requested.get(name, {}) # type: ignore[union-attr]
1298
1347
  next_klass_infos = self.get_related_selections(
1299
1348
  select,
1300
1349
  field_select_mask,
1301
- opts=model._meta,
1350
+ opts=model._model_meta,
1302
1351
  root_alias=alias,
1303
1352
  cur_depth=cur_depth + 1,
1304
1353
  requested=next_requested,
1305
1354
  restricted=restricted,
1306
1355
  )
1307
1356
  get_related_klass_infos(klass_info, next_klass_infos)
1308
- fields_not_found = set(requested).difference(fields_found)
1357
+ fields_not_found = set(requested).difference(fields_found) # type: ignore[arg-type]
1309
1358
  if fields_not_found:
1310
1359
  invalid_fields = (f"'{s}'" for s in fields_not_found)
1311
1360
  raise FieldError(
@@ -1317,13 +1366,13 @@ class SQLCompiler:
1317
1366
  )
1318
1367
  return related_klass_infos
1319
1368
 
1320
- def get_select_for_update_of_arguments(self):
1369
+ def get_select_for_update_of_arguments(self) -> list[str]:
1321
1370
  """
1322
1371
  Return a quoted list of arguments for the SELECT FOR UPDATE OF part of
1323
1372
  the query.
1324
1373
  """
1325
1374
 
1326
- def _get_first_selected_col_from_model(klass_info):
1375
+ def _get_first_selected_col_from_model(klass_info: dict) -> Any | None:
1327
1376
  """
1328
1377
  Find the first selected column from a model. If it doesn't exist,
1329
1378
  don't lock a model.
@@ -1333,10 +1382,11 @@ class SQLCompiler:
1333
1382
  """
1334
1383
  model = klass_info["model"]
1335
1384
  for select_index in klass_info["select_fields"]:
1336
- if self.select[select_index][0].target.model == model:
1337
- return self.select[select_index][0]
1385
+ if self.select[select_index][0].target.model == model: # type: ignore[index]
1386
+ return self.select[select_index][0] # type: ignore[index]
1387
+ return None
1338
1388
 
1339
- def _get_field_choices():
1389
+ def _get_field_choices() -> Generator[str, None, None]:
1340
1390
  """Yield all allowed field paths in breadth-first search order."""
1341
1391
  queue = collections.deque([(None, self.klass_info)])
1342
1392
  while queue:
@@ -1396,7 +1446,7 @@ class SQLCompiler:
1396
1446
  )
1397
1447
  return result
1398
1448
 
1399
- def get_converters(self, expressions):
1449
+ def get_converters(self, expressions: list[Any]) -> dict[int, tuple[list, Any]]:
1400
1450
  converters = {}
1401
1451
  for i, expression in enumerate(expressions):
1402
1452
  if expression:
@@ -1406,11 +1456,13 @@ class SQLCompiler:
1406
1456
  converters[i] = (backend_converters + field_converters, expression)
1407
1457
  return converters
1408
1458
 
1409
- def apply_converters(self, rows, converters):
1459
+ def apply_converters(
1460
+ self, rows: Iterable, converters: dict
1461
+ ) -> Generator[list, None, None]:
1410
1462
  connection = self.connection
1411
- converters = list(converters.items())
1463
+ converters_list = list(converters.items())
1412
1464
  for row in map(list, rows):
1413
- for pos, (convs, expression) in converters:
1465
+ for pos, (convs, expression) in converters_list:
1414
1466
  value = row[pos]
1415
1467
  for converter in convs:
1416
1468
  value = converter(value, expression, connection)
@@ -1419,11 +1471,11 @@ class SQLCompiler:
1419
1471
 
1420
1472
  def results_iter(
1421
1473
  self,
1422
- results=None,
1423
- tuple_expected=False,
1424
- chunked_fetch=False,
1425
- chunk_size=GET_ITERATOR_CHUNK_SIZE,
1426
- ):
1474
+ results: Any = None,
1475
+ tuple_expected: bool = False,
1476
+ chunked_fetch: bool = False,
1477
+ chunk_size: int = GET_ITERATOR_CHUNK_SIZE,
1478
+ ) -> Iterable:
1427
1479
  """Return an iterator over the results from executing this query."""
1428
1480
  if results is None:
1429
1481
  results = self.execute_sql(
@@ -1438,7 +1490,7 @@ class SQLCompiler:
1438
1490
  rows = map(tuple, rows)
1439
1491
  return rows
1440
1492
 
1441
- def has_results(self):
1493
+ def has_results(self) -> bool:
1442
1494
  """
1443
1495
  Backends (e.g. NoSQL) can override this in order to use optimized
1444
1496
  versions of "query has any results."
@@ -1446,8 +1498,11 @@ class SQLCompiler:
1446
1498
  return bool(self.execute_sql(SINGLE))
1447
1499
 
1448
1500
  def execute_sql(
1449
- self, result_type=MULTI, chunked_fetch=False, chunk_size=GET_ITERATOR_CHUNK_SIZE
1450
- ):
1501
+ self,
1502
+ result_type: str = MULTI,
1503
+ chunked_fetch: bool = False,
1504
+ chunk_size: int = GET_ITERATOR_CHUNK_SIZE,
1505
+ ) -> Any:
1451
1506
  """
1452
1507
  Run the query against the database and return the result(s). The
1453
1508
  return value is a single data item if result_type is SINGLE, or an
@@ -1511,7 +1566,9 @@ class SQLCompiler:
1511
1566
  return list(result)
1512
1567
  return result
1513
1568
 
1514
- def as_subquery_condition(self, alias, columns, compiler):
1569
+ def as_subquery_condition(
1570
+ self, alias: str, columns: list[str], compiler: SQLCompiler
1571
+ ) -> tuple[str, tuple]:
1515
1572
  qn = compiler.quote_name_unless_alias
1516
1573
  qn2 = self.connection.ops.quote_name
1517
1574
 
@@ -1523,7 +1580,7 @@ class SQLCompiler:
1523
1580
  sql, params = self.as_sql()
1524
1581
  return f"EXISTS ({sql})", params
1525
1582
 
1526
- def explain_query(self):
1583
+ def explain_query(self) -> Generator[str, None, None]:
1527
1584
  result = list(self.execute_sql())
1528
1585
  # Some backends return 1 item tuples with strings, and others return
1529
1586
  # tuples with integers and strings. Flatten them out into strings.
@@ -1537,10 +1594,10 @@ class SQLCompiler:
1537
1594
 
1538
1595
 
1539
1596
  class SQLInsertCompiler(SQLCompiler):
1540
- returning_fields = None
1541
- returning_params = ()
1597
+ returning_fields: list | None = None
1598
+ returning_params: tuple = ()
1542
1599
 
1543
- def field_as_sql(self, field, val):
1600
+ def field_as_sql(self, field: Any, val: Any) -> tuple[str, list]:
1544
1601
  """
1545
1602
  Take a field and a value intended to be saved on that field, and
1546
1603
  return placeholder SQL and accompanying params. Check for raw values,
@@ -1572,7 +1629,7 @@ class SQLInsertCompiler(SQLCompiler):
1572
1629
 
1573
1630
  return sql, params
1574
1631
 
1575
- def prepare_value(self, field, value):
1632
+ def prepare_value(self, field: Any, value: Any) -> Any:
1576
1633
  """
1577
1634
  Prepare a value to be used in a query by resolving it if it is an
1578
1635
  expression and otherwise calling the field's get_db_prep_save().
@@ -1600,7 +1657,7 @@ class SQLInsertCompiler(SQLCompiler):
1600
1657
  )
1601
1658
  return field.get_db_prep_save(value, connection=self.connection)
1602
1659
 
1603
- def pre_save_val(self, field, obj):
1660
+ def pre_save_val(self, field: Any, obj: Any) -> Any:
1604
1661
  """
1605
1662
  Get the given field's value off the given obj. pre_save() is used for
1606
1663
  things like auto_now on DateTimeField. Skip it if this is a raw query.
@@ -1609,7 +1666,9 @@ class SQLInsertCompiler(SQLCompiler):
1609
1666
  return getattr(obj, field.attname)
1610
1667
  return field.pre_save(obj, add=True)
1611
1668
 
1612
- def assemble_as_sql(self, fields, value_rows):
1669
+ def assemble_as_sql(
1670
+ self, fields: list[Any], value_rows: list[list[Any]]
1671
+ ) -> tuple[list[list[str]], list[list]]:
1613
1672
  """
1614
1673
  Take a sequence of N fields and a sequence of M rows of values, and
1615
1674
  generate placeholder SQL and parameters for each field and value.
@@ -1644,16 +1703,17 @@ class SQLInsertCompiler(SQLCompiler):
1644
1703
 
1645
1704
  return placeholder_rows, param_rows
1646
1705
 
1647
- def as_sql(self):
1706
+ def as_sql(self) -> list[tuple[str, tuple]]:
1648
1707
  # We don't need quote_name_unless_alias() here, since these are all
1649
1708
  # going to be column names (so we can avoid the extra overhead).
1650
1709
  qn = self.connection.ops.quote_name
1651
- opts = self.query.get_meta()
1710
+ meta = self.query.get_model_meta()
1711
+ options = self.query.model.model_options
1652
1712
  insert_statement = self.connection.ops.insert_statement(
1653
1713
  on_conflict=self.query.on_conflict,
1654
1714
  )
1655
- result = [f"{insert_statement} {qn(opts.db_table)}"]
1656
- fields = self.query.fields or [opts.get_field("id")]
1715
+ result = [f"{insert_statement} {qn(options.db_table)}"]
1716
+ fields = self.query.fields or [meta.get_field("id")]
1657
1717
  result.append("({})".format(", ".join(qn(f.column) for f in fields)))
1658
1718
 
1659
1719
  if self.query.fields:
@@ -1724,13 +1784,14 @@ class SQLInsertCompiler(SQLCompiler):
1724
1784
  for p, vals in zip(placeholder_rows, param_rows)
1725
1785
  ]
1726
1786
 
1727
- def execute_sql(self, returning_fields=None):
1787
+ def execute_sql(self, returning_fields: list | None = None) -> list:
1728
1788
  assert not (
1729
1789
  returning_fields
1730
1790
  and len(self.query.objs) != 1
1731
1791
  and not self.connection.features.can_return_rows_from_bulk_insert
1732
1792
  )
1733
- opts = self.query.get_meta()
1793
+ meta = self.query.get_model_meta()
1794
+ options = self.query.model.model_options
1734
1795
  self.returning_fields = returning_fields
1735
1796
  with self.connection.cursor() as cursor:
1736
1797
  for sql, params in self.as_sql():
@@ -1755,12 +1816,12 @@ class SQLInsertCompiler(SQLCompiler):
1755
1816
  (
1756
1817
  self.connection.ops.last_insert_id(
1757
1818
  cursor,
1758
- opts.db_table,
1759
- opts.get_field("id").column,
1819
+ options.db_table,
1820
+ meta.get_field("id").column,
1760
1821
  ),
1761
1822
  )
1762
1823
  ]
1763
- cols = [field.get_col(opts.db_table) for field in self.returning_fields]
1824
+ cols = [field.get_col(options.db_table) for field in self.returning_fields]
1764
1825
  converters = self.get_converters(cols)
1765
1826
  if converters:
1766
1827
  rows = list(self.apply_converters(rows, converters))
@@ -1769,13 +1830,13 @@ class SQLInsertCompiler(SQLCompiler):
1769
1830
 
1770
1831
  class SQLDeleteCompiler(SQLCompiler):
1771
1832
  @cached_property
1772
- def single_alias(self):
1833
+ def single_alias(self) -> bool:
1773
1834
  # Ensure base table is in aliases.
1774
1835
  self.query.get_initial_alias()
1775
1836
  return sum(self.query.alias_refcount[t] > 0 for t in self.query.alias_map) == 1
1776
1837
 
1777
1838
  @classmethod
1778
- def _expr_refs_base_model(cls, expr, base_model):
1839
+ def _expr_refs_base_model(cls, expr: Any, base_model: Any) -> bool:
1779
1840
  if isinstance(expr, Query):
1780
1841
  return expr.model == base_model
1781
1842
  if not hasattr(expr, "get_source_expressions"):
@@ -1786,7 +1847,7 @@ class SQLDeleteCompiler(SQLCompiler):
1786
1847
  )
1787
1848
 
1788
1849
  @cached_property
1789
- def contains_self_reference_subquery(self):
1850
+ def contains_self_reference_subquery(self) -> bool:
1790
1851
  return any(
1791
1852
  self._expr_refs_base_model(expr, self.query.model)
1792
1853
  for expr in chain(
@@ -1794,7 +1855,7 @@ class SQLDeleteCompiler(SQLCompiler):
1794
1855
  )
1795
1856
  )
1796
1857
 
1797
- def _as_sql(self, query):
1858
+ def _as_sql(self, query: Query) -> tuple[str, tuple]:
1798
1859
  delete = f"DELETE FROM {self.quote_name_unless_alias(query.base_table)}"
1799
1860
  try:
1800
1861
  where, params = self.compile(query.where)
@@ -1802,7 +1863,7 @@ class SQLDeleteCompiler(SQLCompiler):
1802
1863
  return delete, ()
1803
1864
  return f"{delete} WHERE {where}", tuple(params)
1804
1865
 
1805
- def as_sql(self):
1866
+ def as_sql(self) -> tuple[str, tuple]:
1806
1867
  """
1807
1868
  Create the SQL for this query. Return the SQL string and list of
1808
1869
  parameters.
@@ -1812,7 +1873,7 @@ class SQLDeleteCompiler(SQLCompiler):
1812
1873
  innerq = self.query.clone()
1813
1874
  innerq.__class__ = Query
1814
1875
  innerq.clear_select_clause()
1815
- id_field = self.query.model._meta.get_field("id")
1876
+ id_field = self.query.model._model_meta.get_field("id")
1816
1877
  innerq.select = [id_field.get_col(self.query.get_initial_alias())]
1817
1878
  outerq = Query(self.query.model)
1818
1879
  if not self.connection.features.update_can_self_select:
@@ -1825,7 +1886,7 @@ class SQLDeleteCompiler(SQLCompiler):
1825
1886
 
1826
1887
 
1827
1888
  class SQLUpdateCompiler(SQLCompiler):
1828
- def as_sql(self):
1889
+ def as_sql(self) -> tuple[str, tuple]:
1829
1890
  """
1830
1891
  Create the SQL for this query. Return the SQL string and list of
1831
1892
  parameters.
@@ -1888,7 +1949,7 @@ class SQLUpdateCompiler(SQLCompiler):
1888
1949
  result.append(f"WHERE {where}")
1889
1950
  return " ".join(result), tuple(update_params + params)
1890
1951
 
1891
- def execute_sql(self, result_type):
1952
+ def execute_sql(self, result_type: str) -> int:
1892
1953
  """
1893
1954
  Execute the specified update. Return the number of rows affected by
1894
1955
  the primary update query. The "primary update query" is the first
@@ -1909,7 +1970,7 @@ class SQLUpdateCompiler(SQLCompiler):
1909
1970
  is_empty = False
1910
1971
  return rows
1911
1972
 
1912
- def pre_sql_setup(self):
1973
+ def pre_sql_setup(self) -> None:
1913
1974
  """
1914
1975
  If the update depends on results from other tables, munge the "where"
1915
1976
  conditions to match the format required for (portable) SQL updates.
@@ -1965,7 +2026,7 @@ class SQLUpdateCompiler(SQLCompiler):
1965
2026
 
1966
2027
 
1967
2028
  class SQLAggregateCompiler(SQLCompiler):
1968
- def as_sql(self):
2029
+ def as_sql(self) -> tuple[str, tuple]:
1969
2030
  """
1970
2031
  Create the SQL for this query. Return the SQL string and list of
1971
2032
  parameters.
@@ -1988,7 +2049,9 @@ class SQLAggregateCompiler(SQLCompiler):
1988
2049
  return sql, params
1989
2050
 
1990
2051
 
1991
- def cursor_iter(cursor, sentinel, col_count, itersize):
2052
+ def cursor_iter(
2053
+ cursor: Any, sentinel: Any, col_count: int | None, itersize: int
2054
+ ) -> Generator[list, None, None]:
1992
2055
  """
1993
2056
  Yield blocks of rows from a cursor and ensure the cursor is closed when
1994
2057
  done.