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/sql/query.py CHANGED
@@ -7,15 +7,19 @@ databases). The abstraction barrier only works one way: this module has to know
7
7
  all about the internals of models in order to get the information it needs.
8
8
  """
9
9
 
10
+ from __future__ import annotations
11
+
10
12
  import copy
11
13
  import difflib
12
14
  import functools
13
15
  import sys
14
16
  from collections import Counter, namedtuple
15
17
  from collections.abc import Iterator, Mapping
18
+ from collections.abc import Iterator as TypingIterator
16
19
  from functools import cached_property
17
20
  from itertools import chain, count, product
18
21
  from string import ascii_uppercase
22
+ from typing import TYPE_CHECKING, Any
19
23
 
20
24
  from plain.models.aggregates import Count
21
25
  from plain.models.constants import LOOKUP_SEP
@@ -45,6 +49,13 @@ from plain.models.sql.where import AND, OR, ExtraWhere, NothingNode, WhereNode
45
49
  from plain.utils.regex_helper import _lazy_re_compile
46
50
  from plain.utils.tree import Node
47
51
 
52
+ if TYPE_CHECKING:
53
+ from plain.models import Model
54
+ from plain.models.backends.base.base import BaseDatabaseWrapper
55
+ from plain.models.meta import Meta
56
+ from plain.models.sql.compiler import SQLCompiler
57
+
58
+
48
59
  __all__ = ["Query", "RawQuery"]
49
60
 
50
61
  # Quotation marks ('"`[]), whitespace characters, semicolons, or inline
@@ -56,17 +67,17 @@ FORBIDDEN_ALIAS_PATTERN = _lazy_re_compile(r"['`\"\]\[;\s]|--|/\*|\*/")
56
67
  EXPLAIN_OPTIONS_PATTERN = _lazy_re_compile(r"[\w\-]+")
57
68
 
58
69
 
59
- def get_field_names_from_opts(opts):
60
- if opts is None:
70
+ def get_field_names_from_opts(meta: Meta | None) -> set[str]:
71
+ if meta is None:
61
72
  return set()
62
73
  return set(
63
74
  chain.from_iterable(
64
- (f.name, f.attname) if f.concrete else (f.name,) for f in opts.get_fields()
75
+ (f.name, f.attname) if f.concrete else (f.name,) for f in meta.get_fields()
65
76
  )
66
77
  )
67
78
 
68
79
 
69
- def get_children_from_q(q):
80
+ def get_children_from_q(q: Q) -> TypingIterator[tuple[str, Any]]:
70
81
  for child in q.children:
71
82
  if isinstance(child, Node):
72
83
  yield from get_children_from_q(child)
@@ -76,14 +87,14 @@ def get_children_from_q(q):
76
87
 
77
88
  JoinInfo = namedtuple(
78
89
  "JoinInfo",
79
- ("final_field", "targets", "opts", "joins", "path", "transform_function"),
90
+ ("final_field", "targets", "meta", "joins", "path", "transform_function"),
80
91
  )
81
92
 
82
93
 
83
94
  class RawQuery:
84
95
  """A single raw SQL query."""
85
96
 
86
- def __init__(self, sql, params=()):
97
+ def __init__(self, sql: str, params: tuple[Any, ...] | dict[str, Any] = ()):
87
98
  self.params = params
88
99
  self.sql = sql
89
100
  self.cursor = None
@@ -94,19 +105,22 @@ class RawQuery:
94
105
  self.extra_select = {}
95
106
  self.annotation_select = {}
96
107
 
97
- def chain(self):
108
+ def chain(self) -> RawQuery:
98
109
  return self.clone()
99
110
 
100
- def clone(self):
111
+ def clone(self) -> RawQuery:
101
112
  return RawQuery(self.sql, params=self.params)
102
113
 
103
- def get_columns(self):
114
+ def get_columns(self) -> list[str]:
104
115
  if self.cursor is None:
105
116
  self._execute_query()
106
117
  converter = db_connection.introspection.identifier_converter
107
- return [converter(column_meta[0]) for column_meta in self.cursor.description]
118
+ return [
119
+ converter(column_model_meta[0])
120
+ for column_model_meta in self.cursor.description
121
+ ]
108
122
 
109
- def __iter__(self):
123
+ def __iter__(self) -> TypingIterator[Any]:
110
124
  # Always execute a new query for a new iterator.
111
125
  # This could be optimized with a cache at the expense of RAM.
112
126
  self._execute_query()
@@ -118,21 +132,21 @@ class RawQuery:
118
132
  result = self.cursor
119
133
  return iter(result)
120
134
 
121
- def __repr__(self):
135
+ def __repr__(self) -> str:
122
136
  return f"<{self.__class__.__name__}: {self}>"
123
137
 
124
138
  @property
125
- def params_type(self):
139
+ def params_type(self) -> type[dict] | type[tuple] | None:
126
140
  if self.params is None:
127
141
  return None
128
142
  return dict if isinstance(self.params, Mapping) else tuple
129
143
 
130
- def __str__(self):
144
+ def __str__(self) -> str:
131
145
  if self.params_type is None:
132
146
  return self.sql
133
147
  return self.sql % self.params_type(self.params)
134
148
 
135
- def _execute_query(self):
149
+ def _execute_query(self) -> None:
136
150
  # Adapt parameters to the database, as much as possible considering
137
151
  # that the target type isn't known. See #17755.
138
152
  params_type = self.params_type
@@ -226,7 +240,7 @@ class Query(BaseExpression):
226
240
 
227
241
  explain_info = None
228
242
 
229
- def __init__(self, model, alias_cols=True):
243
+ def __init__(self, model: type[Model], alias_cols: bool = True):
230
244
  self.model = model
231
245
  self.alias_refcount = {}
232
246
  # alias_map is the most important data structure regarding joins.
@@ -255,7 +269,7 @@ class Query(BaseExpression):
255
269
  self._filtered_relations = {}
256
270
 
257
271
  @property
258
- def output_field(self):
272
+ def output_field(self) -> Field | None: # type: ignore[return]
259
273
  if len(self.select) == 1:
260
274
  select = self.select[0]
261
275
  return getattr(select, "target", None) or select.field
@@ -263,11 +277,11 @@ class Query(BaseExpression):
263
277
  return next(iter(self.annotation_select.values())).output_field
264
278
 
265
279
  @cached_property
266
- def base_table(self):
280
+ def base_table(self) -> str | None:
267
281
  for alias in self.alias_map:
268
282
  return alias
269
283
 
270
- def __str__(self):
284
+ def __str__(self) -> str:
271
285
  """
272
286
  Return the query as a string of SQL with the parameter values
273
287
  substituted in (use sql_with_params() to see the unsubstituted string).
@@ -278,53 +292,52 @@ class Query(BaseExpression):
278
292
  sql, params = self.sql_with_params()
279
293
  return sql % params
280
294
 
281
- def sql_with_params(self):
295
+ def sql_with_params(self) -> tuple[str, tuple[Any, ...]]:
282
296
  """
283
297
  Return the query as an SQL string and the parameters that will be
284
298
  substituted into the query.
285
299
  """
286
300
  return self.get_compiler().as_sql()
287
301
 
288
- def __deepcopy__(self, memo):
302
+ def __deepcopy__(self, memo: dict[int, Any]) -> Query:
289
303
  """Limit the amount of work when a Query is deepcopied."""
290
304
  result = self.clone()
291
305
  memo[id(self)] = result
292
306
  return result
293
307
 
294
- def get_compiler(self, *, elide_empty=True):
308
+ def get_compiler(self, *, elide_empty: bool = True) -> Any:
295
309
  return db_connection.ops.compiler(self.compiler)(
296
310
  self, db_connection, elide_empty
297
311
  )
298
312
 
299
- def get_meta(self):
313
+ def get_model_meta(self) -> Meta:
300
314
  """
301
- Return the Options instance (the model._meta) from which to start
302
- processing. Normally, this is self.model._meta, but it can be changed
315
+ Return the Meta instance (the model._model_meta) from which to start
316
+ processing. Normally, this is self.model._model_meta, but it can be changed
303
317
  by subclasses.
304
318
  """
305
- if self.model:
306
- return self.model._meta
319
+ return self.model._model_meta
307
320
 
308
- def clone(self):
321
+ def clone(self) -> Query:
309
322
  """
310
323
  Return a copy of the current Query. A lightweight alternative to
311
324
  deepcopy().
312
325
  """
313
- obj = Empty()
314
- obj.__class__ = self.__class__
326
+ obj = Empty() # type: ignore[misc]
327
+ obj.__class__ = self.__class__ # type: ignore[misc]
315
328
  # Copy references to everything.
316
- obj.__dict__ = self.__dict__.copy()
329
+ obj.__dict__ = self.__dict__.copy() # type: ignore[attr-defined]
317
330
  # Clone attributes that can't use shallow copy.
318
- obj.alias_refcount = self.alias_refcount.copy()
319
- obj.alias_map = self.alias_map.copy()
320
- obj.external_aliases = self.external_aliases.copy()
321
- obj.table_map = self.table_map.copy()
322
- obj.where = self.where.clone()
323
- obj.annotations = self.annotations.copy()
331
+ obj.alias_refcount = self.alias_refcount.copy() # type: ignore[attr-defined]
332
+ obj.alias_map = self.alias_map.copy() # type: ignore[attr-defined]
333
+ obj.external_aliases = self.external_aliases.copy() # type: ignore[attr-defined]
334
+ obj.table_map = self.table_map.copy() # type: ignore[attr-defined]
335
+ obj.where = self.where.clone() # type: ignore[attr-defined]
336
+ obj.annotations = self.annotations.copy() # type: ignore[attr-defined]
324
337
  if self.annotation_select_mask is not None:
325
- obj.annotation_select_mask = self.annotation_select_mask.copy()
338
+ obj.annotation_select_mask = self.annotation_select_mask.copy() # type: ignore[attr-defined]
326
339
  if self.combined_queries:
327
- obj.combined_queries = tuple(
340
+ obj.combined_queries = tuple( # type: ignore[attr-defined]
328
341
  [query.clone() for query in self.combined_queries]
329
342
  )
330
343
  # _annotation_select_cache cannot be copied, as doing so breaks the
@@ -332,50 +345,50 @@ class Query(BaseExpression):
332
345
  # _annotation_select_cache point to the same underlying objects.
333
346
  # It will get re-populated in the cloned queryset the next time it's
334
347
  # used.
335
- obj._annotation_select_cache = None
336
- obj.extra = self.extra.copy()
348
+ obj._annotation_select_cache = None # type: ignore[attr-defined]
349
+ obj.extra = self.extra.copy() # type: ignore[attr-defined]
337
350
  if self.extra_select_mask is not None:
338
- obj.extra_select_mask = self.extra_select_mask.copy()
351
+ obj.extra_select_mask = self.extra_select_mask.copy() # type: ignore[attr-defined]
339
352
  if self._extra_select_cache is not None:
340
- obj._extra_select_cache = self._extra_select_cache.copy()
353
+ obj._extra_select_cache = self._extra_select_cache.copy() # type: ignore[attr-defined]
341
354
  if self.select_related is not False:
342
355
  # Use deepcopy because select_related stores fields in nested
343
356
  # dicts.
344
- obj.select_related = copy.deepcopy(obj.select_related)
357
+ obj.select_related = copy.deepcopy(obj.select_related) # type: ignore[attr-defined]
345
358
  if "subq_aliases" in self.__dict__:
346
- obj.subq_aliases = self.subq_aliases.copy()
347
- obj.used_aliases = self.used_aliases.copy()
348
- obj._filtered_relations = self._filtered_relations.copy()
359
+ obj.subq_aliases = self.subq_aliases.copy() # type: ignore[attr-defined]
360
+ obj.used_aliases = self.used_aliases.copy() # type: ignore[attr-defined]
361
+ obj._filtered_relations = self._filtered_relations.copy() # type: ignore[attr-defined]
349
362
  # Clear the cached_property, if it exists.
350
363
  obj.__dict__.pop("base_table", None)
351
- return obj
364
+ return obj # type: ignore[return-value]
352
365
 
353
- def chain(self, klass=None):
366
+ def chain(self, klass: type[Query] | None = None) -> Query:
354
367
  """
355
368
  Return a copy of the current Query that's ready for another operation.
356
369
  The klass argument changes the type of the Query, e.g. UpdateQuery.
357
370
  """
358
371
  obj = self.clone()
359
372
  if klass and obj.__class__ != klass:
360
- obj.__class__ = klass
373
+ obj.__class__ = klass # type: ignore[misc]
361
374
  if not obj.filter_is_sticky:
362
- obj.used_aliases = set()
375
+ obj.used_aliases = set() # type: ignore[attr-defined]
363
376
  obj.filter_is_sticky = False
364
377
  if hasattr(obj, "_setup_query"):
365
- obj._setup_query()
366
- return obj
378
+ obj._setup_query() # type: ignore[attr-defined]
379
+ return obj # type: ignore[return-value]
367
380
 
368
- def relabeled_clone(self, change_map):
381
+ def relabeled_clone(self, change_map: dict[str, str]) -> Query:
369
382
  clone = self.clone()
370
383
  clone.change_aliases(change_map)
371
384
  return clone
372
385
 
373
- def _get_col(self, target, field, alias):
386
+ def _get_col(self, target: Any, field: Field, alias: str | None) -> Col:
374
387
  if not self.alias_cols:
375
388
  alias = None
376
389
  return target.get_col(alias, field)
377
390
 
378
- def get_aggregation(self, aggregate_exprs):
391
+ def get_aggregation(self, aggregate_exprs: dict[str, Any]) -> dict[str, Any]:
379
392
  """
380
393
  Return the dictionary with the values of the existing aggregations.
381
394
  """
@@ -438,7 +451,7 @@ class Query(BaseExpression):
438
451
  # used.
439
452
  if inner_query.default_cols and has_existing_aggregation:
440
453
  inner_query.group_by = (
441
- self.model._meta.get_field("id").get_col(
454
+ self.model._model_meta.get_field("id").get_col(
442
455
  inner_query.get_initial_alias()
443
456
  ),
444
457
  )
@@ -482,7 +495,7 @@ class Query(BaseExpression):
482
495
  # field selected in the inner query, yet we must use a subquery.
483
496
  # So, make sure at least one field is selected.
484
497
  inner_query.select = (
485
- self.model._meta.get_field("id").get_col(
498
+ self.model._model_meta.get_field("id").get_col(
486
499
  inner_query.get_initial_alias()
487
500
  ),
488
501
  )
@@ -526,22 +539,22 @@ class Query(BaseExpression):
526
539
 
527
540
  return dict(zip(outer_query.annotation_select, result))
528
541
 
529
- def get_count(self):
542
+ def get_count(self) -> int:
530
543
  """
531
544
  Perform a COUNT() query using the current filter constraints.
532
545
  """
533
546
  obj = self.clone()
534
547
  return obj.get_aggregation({"__count": Count("*")})["__count"]
535
548
 
536
- def has_filters(self):
549
+ def has_filters(self) -> bool:
537
550
  return self.where
538
551
 
539
- def exists(self, limit=True):
552
+ def exists(self, limit: bool = True) -> Query:
540
553
  q = self.clone()
541
554
  if not (q.distinct and q.is_sliced):
542
555
  if q.group_by is True:
543
556
  q.add_fields(
544
- (f.attname for f in self.model._meta.concrete_fields), False
557
+ (f.attname for f in self.model._model_meta.concrete_fields), False
545
558
  )
546
559
  # Disable GROUP BY aliases to avoid orphaning references to the
547
560
  # SELECT clause which is about to be cleared.
@@ -558,12 +571,12 @@ class Query(BaseExpression):
558
571
  q.add_annotation(Value(1), "a")
559
572
  return q
560
573
 
561
- def has_results(self):
574
+ def has_results(self) -> bool:
562
575
  q = self.exists()
563
576
  compiler = q.get_compiler()
564
577
  return compiler.has_results()
565
578
 
566
- def explain(self, format=None, **options):
579
+ def explain(self, format: str | None = None, **options: Any) -> str:
567
580
  q = self.clone()
568
581
  for option_name in options:
569
582
  if (
@@ -575,7 +588,7 @@ class Query(BaseExpression):
575
588
  compiler = q.get_compiler()
576
589
  return "\n".join(compiler.explain_query())
577
590
 
578
- def combine(self, rhs, connector):
591
+ def combine(self, rhs: Query, connector: str) -> None:
579
592
  """
580
593
  Merge the 'rhs' query into the current one (with any 'rhs' effects
581
594
  being applied *after* (that is, "to the right of") anything in the
@@ -690,15 +703,20 @@ class Query(BaseExpression):
690
703
  self.order_by = rhs.order_by or self.order_by
691
704
  self.extra_order_by = rhs.extra_order_by or self.extra_order_by
692
705
 
693
- def _get_defer_select_mask(self, opts, mask, select_mask=None):
706
+ def _get_defer_select_mask(
707
+ self,
708
+ meta: Meta,
709
+ mask: dict[str, Any],
710
+ select_mask: dict[Any, Any] | None = None,
711
+ ) -> dict[Any, Any]:
694
712
  if select_mask is None:
695
713
  select_mask = {}
696
- select_mask[opts.get_field("id")] = {}
714
+ select_mask[meta.get_field("id")] = {}
697
715
  # All concrete fields that are not part of the defer mask must be
698
716
  # loaded. If a relational field is encountered it gets added to the
699
717
  # mask for it be considered if `select_related` and the cycle continues
700
718
  # by recursively calling this function.
701
- for field in opts.concrete_fields:
719
+ for field in meta.concrete_fields:
702
720
  field_mask = mask.pop(field.name, None)
703
721
  if field_mask is None:
704
722
  select_mask.setdefault(field, {})
@@ -708,43 +726,48 @@ class Query(BaseExpression):
708
726
  field_select_mask = select_mask.setdefault(field, {})
709
727
  related_model = field.remote_field.model
710
728
  self._get_defer_select_mask(
711
- related_model._meta, field_mask, field_select_mask
729
+ related_model._model_meta, field_mask, field_select_mask
712
730
  )
713
731
  # Remaining defer entries must be references to reverse relationships.
714
732
  # The following code is expected to raise FieldError if it encounters
715
733
  # a malformed defer entry.
716
734
  for field_name, field_mask in mask.items():
717
735
  if filtered_relation := self._filtered_relations.get(field_name):
718
- relation = opts.get_field(filtered_relation.relation_name)
736
+ relation = meta.get_field(filtered_relation.relation_name)
719
737
  field_select_mask = select_mask.setdefault((field_name, relation), {})
720
738
  field = relation.field
721
739
  else:
722
- field = opts.get_field(field_name).field
740
+ field = meta.get_field(field_name).field
723
741
  field_select_mask = select_mask.setdefault(field, {})
724
742
  related_model = field.model
725
743
  self._get_defer_select_mask(
726
- related_model._meta, field_mask, field_select_mask
744
+ related_model._model_meta, field_mask, field_select_mask
727
745
  )
728
746
  return select_mask
729
747
 
730
- def _get_only_select_mask(self, opts, mask, select_mask=None):
748
+ def _get_only_select_mask(
749
+ self,
750
+ meta: Meta,
751
+ mask: dict[str, Any],
752
+ select_mask: dict[Any, Any] | None = None,
753
+ ) -> dict[Any, Any]:
731
754
  if select_mask is None:
732
755
  select_mask = {}
733
- select_mask[opts.get_field("id")] = {}
756
+ select_mask[meta.get_field("id")] = {}
734
757
  # Only include fields mentioned in the mask.
735
758
  for field_name, field_mask in mask.items():
736
- field = opts.get_field(field_name)
759
+ field = meta.get_field(field_name)
737
760
  field_select_mask = select_mask.setdefault(field, {})
738
761
  if field_mask:
739
762
  if not field.is_relation:
740
763
  raise FieldError(next(iter(field_mask)))
741
764
  related_model = field.remote_field.model
742
765
  self._get_only_select_mask(
743
- related_model._meta, field_mask, field_select_mask
766
+ related_model._model_meta, field_mask, field_select_mask
744
767
  )
745
768
  return select_mask
746
769
 
747
- def get_select_mask(self):
770
+ def get_select_mask(self) -> dict[Any, Any]:
748
771
  """
749
772
  Convert the self.deferred_loading data structure to an alternate data
750
773
  structure, describing the field that *will* be loaded. This is used to
@@ -761,12 +784,14 @@ class Query(BaseExpression):
761
784
  part_mask = mask
762
785
  for part in field_name.split(LOOKUP_SEP):
763
786
  part_mask = part_mask.setdefault(part, {})
764
- opts = self.get_meta()
787
+ meta = self.get_model_meta()
765
788
  if defer:
766
- return self._get_defer_select_mask(opts, mask)
767
- return self._get_only_select_mask(opts, mask)
789
+ return self._get_defer_select_mask(meta, mask)
790
+ return self._get_only_select_mask(meta, mask)
768
791
 
769
- def table_alias(self, table_name, create=False, filtered_relation=None):
792
+ def table_alias(
793
+ self, table_name: str, create: bool = False, filtered_relation: Any = None
794
+ ) -> tuple[str, bool]:
770
795
  """
771
796
  Return a table alias for the given table_name and whether this is a
772
797
  new alias or not.
@@ -793,15 +818,15 @@ class Query(BaseExpression):
793
818
  self.alias_refcount[alias] = 1
794
819
  return alias, True
795
820
 
796
- def ref_alias(self, alias):
821
+ def ref_alias(self, alias: str) -> None:
797
822
  """Increases the reference count for this alias."""
798
823
  self.alias_refcount[alias] += 1
799
824
 
800
- def unref_alias(self, alias, amount=1):
825
+ def unref_alias(self, alias: str, amount: int = 1) -> None:
801
826
  """Decreases the reference count for this alias."""
802
827
  self.alias_refcount[alias] -= amount
803
828
 
804
- def promote_joins(self, aliases):
829
+ def promote_joins(self, aliases: set[str] | list[str]) -> None:
805
830
  """
806
831
  Promote recursively the join type of given aliases and its children to
807
832
  an outer join. If 'unconditional' is False, only promote the join if
@@ -838,7 +863,7 @@ class Query(BaseExpression):
838
863
  and join not in aliases
839
864
  )
840
865
 
841
- def demote_joins(self, aliases):
866
+ def demote_joins(self, aliases: set[str] | list[str]) -> None:
842
867
  """
843
868
  Change join type from LOUTER to INNER for all joins in aliases.
844
869
 
@@ -857,7 +882,7 @@ class Query(BaseExpression):
857
882
  if self.alias_map[parent_alias].join_type == INNER:
858
883
  aliases.append(parent_alias)
859
884
 
860
- def reset_refcounts(self, to_counts):
885
+ def reset_refcounts(self, to_counts: dict[str, int]) -> None:
861
886
  """
862
887
  Reset reference counts for aliases so that they match the value passed
863
888
  in `to_counts`.
@@ -866,7 +891,7 @@ class Query(BaseExpression):
866
891
  unref_amount = cur_refcount - to_counts.get(alias, 0)
867
892
  self.unref_alias(alias, unref_amount)
868
893
 
869
- def change_aliases(self, change_map):
894
+ def change_aliases(self, change_map: dict[str, str]) -> None:
870
895
  """
871
896
  Change the aliases in change_map (which maps old-alias -> new-alias),
872
897
  relabelling any references to them in select columns and the where
@@ -911,7 +936,9 @@ class Query(BaseExpression):
911
936
  for alias, aliased in self.external_aliases.items()
912
937
  }
913
938
 
914
- def bump_prefix(self, other_query, exclude=None):
939
+ def bump_prefix(
940
+ self, other_query: Query, exclude: set[str] | dict[str, str] | None = None
941
+ ) -> None:
915
942
  """
916
943
  Change the alias prefix to the next letter in the alphabet in a way
917
944
  that the other query's aliases and this query's aliases will not
@@ -919,7 +946,7 @@ class Query(BaseExpression):
919
946
  after this call. To prevent changing aliases use the exclude parameter.
920
947
  """
921
948
 
922
- def prefix_gen():
949
+ def prefix_gen() -> TypingIterator[str]:
923
950
  """
924
951
  Generate a sequence of characters in alphabetical order:
925
952
  -> 'A', 'B', 'C', ...
@@ -966,7 +993,7 @@ class Query(BaseExpression):
966
993
  }
967
994
  )
968
995
 
969
- def get_initial_alias(self):
996
+ def get_initial_alias(self) -> str | None:
970
997
  """
971
998
  Return the first alias for this query, after increasing its reference
972
999
  count.
@@ -975,12 +1002,14 @@ class Query(BaseExpression):
975
1002
  alias = self.base_table
976
1003
  self.ref_alias(alias)
977
1004
  elif self.model:
978
- alias = self.join(self.base_table_class(self.get_meta().db_table, None))
1005
+ alias = self.join(
1006
+ self.base_table_class(self.model.model_options.db_table, None)
1007
+ )
979
1008
  else:
980
1009
  alias = None
981
1010
  return alias
982
1011
 
983
- def count_active_tables(self):
1012
+ def count_active_tables(self) -> int:
984
1013
  """
985
1014
  Return the number of tables in this query with a non-zero reference
986
1015
  count. After execution, the reference counts are zeroed, so tables
@@ -988,7 +1017,12 @@ class Query(BaseExpression):
988
1017
  """
989
1018
  return len([1 for count in self.alias_refcount.values() if count])
990
1019
 
991
- def join(self, join, reuse=None, reuse_with_filtered_relation=False):
1020
+ def join(
1021
+ self,
1022
+ join: BaseTable | Join,
1023
+ reuse: set[str] | None = None,
1024
+ reuse_with_filtered_relation: bool = False,
1025
+ ) -> str:
992
1026
  """
993
1027
  Return an alias for the 'join', either reusing an existing alias for
994
1028
  that join or creating a new one. 'join' is either a base_table_class or
@@ -1029,7 +1063,7 @@ class Query(BaseExpression):
1029
1063
  join.table_name, create=True, filtered_relation=join.filtered_relation
1030
1064
  )
1031
1065
  if join.join_type:
1032
- if self.alias_map[join.parent_alias].join_type == LOUTER or join.nullable:
1066
+ if self.alias_map[join.parent_alias].join_type == LOUTER or join.nullable: # type: ignore[attr-defined]
1033
1067
  join_type = LOUTER
1034
1068
  else:
1035
1069
  join_type = INNER
@@ -1038,14 +1072,16 @@ class Query(BaseExpression):
1038
1072
  self.alias_map[alias] = join
1039
1073
  return alias
1040
1074
 
1041
- def check_alias(self, alias):
1075
+ def check_alias(self, alias: str) -> None:
1042
1076
  if FORBIDDEN_ALIAS_PATTERN.search(alias):
1043
1077
  raise ValueError(
1044
1078
  "Column aliases cannot contain whitespace characters, quotation marks, "
1045
1079
  "semicolons, or SQL comments."
1046
1080
  )
1047
1081
 
1048
- def add_annotation(self, annotation, alias, select=True):
1082
+ def add_annotation(
1083
+ self, annotation: BaseExpression, alias: str, select: bool = True
1084
+ ) -> None:
1049
1085
  """Add a single annotation expression to the Query."""
1050
1086
  self.check_alias(alias)
1051
1087
  annotation = annotation.resolve_expression(self, allow_joins=True, reuse=None)
@@ -1055,7 +1091,7 @@ class Query(BaseExpression):
1055
1091
  self.set_annotation_mask(set(self.annotation_select).difference({alias}))
1056
1092
  self.annotations[alias] = annotation
1057
1093
 
1058
- def resolve_expression(self, query, *args, **kwargs):
1094
+ def resolve_expression(self, query: Query, *args: Any, **kwargs: Any) -> Query:
1059
1095
  clone = self.clone()
1060
1096
  # Subqueries need to use a different set of aliases than the outer query.
1061
1097
  clone.bump_prefix(query)
@@ -1078,13 +1114,13 @@ class Query(BaseExpression):
1078
1114
  for alias, table in query.alias_map.items():
1079
1115
  clone.external_aliases[alias] = (
1080
1116
  isinstance(table, Join)
1081
- and table.join_field.related_model._meta.db_table != alias
1117
+ and table.join_field.related_model.model_options.db_table != alias
1082
1118
  ) or (
1083
1119
  isinstance(table, BaseTable) and table.table_name != table.table_alias
1084
1120
  )
1085
1121
  return clone
1086
1122
 
1087
- def get_external_cols(self):
1123
+ def get_external_cols(self) -> list[Col]:
1088
1124
  exprs = chain(self.annotations.values(), self.where.children)
1089
1125
  return [
1090
1126
  col
@@ -1092,7 +1128,9 @@ class Query(BaseExpression):
1092
1128
  if col.alias in self.external_aliases
1093
1129
  ]
1094
1130
 
1095
- def get_group_by_cols(self, wrapper=None):
1131
+ def get_group_by_cols(
1132
+ self, wrapper: BaseExpression | None = None
1133
+ ) -> list[Col] | list[BaseExpression]:
1096
1134
  # If wrapper is referenced by an alias for an explicit GROUP BY through
1097
1135
  # values() a reference to this expression and not the self must be
1098
1136
  # returned to ensure external column references are not grouped against
@@ -1102,7 +1140,9 @@ class Query(BaseExpression):
1102
1140
  return [wrapper or self]
1103
1141
  return external_cols
1104
1142
 
1105
- def as_sql(self, compiler, connection):
1143
+ def as_sql(
1144
+ self, compiler: SQLCompiler, connection: BaseDatabaseWrapper
1145
+ ) -> tuple[str, tuple[Any, ...]]:
1106
1146
  # Some backends (e.g. Oracle) raise an error when a subquery contains
1107
1147
  # unnecessary ORDER BY clause.
1108
1148
  if (
@@ -1117,7 +1157,9 @@ class Query(BaseExpression):
1117
1157
  sql = f"({sql})"
1118
1158
  return sql, params
1119
1159
 
1120
- def resolve_lookup_value(self, value, can_reuse, allow_joins):
1160
+ def resolve_lookup_value(
1161
+ self, value: Any, can_reuse: set[str] | None, allow_joins: bool
1162
+ ) -> Any:
1121
1163
  if hasattr(value, "resolve_expression"):
1122
1164
  value = value.resolve_expression(
1123
1165
  self,
@@ -1137,7 +1179,9 @@ class Query(BaseExpression):
1137
1179
  return type_(values)
1138
1180
  return value
1139
1181
 
1140
- def solve_lookup_type(self, lookup, summarize=False):
1182
+ def solve_lookup_type(
1183
+ self, lookup: str, summarize: bool = False
1184
+ ) -> tuple[list[str] | tuple[str, ...], tuple[str, ...], BaseExpression | bool]:
1141
1185
  """
1142
1186
  Solve the lookup type from the lookup (e.g.: 'foobar__id__icontains').
1143
1187
  """
@@ -1151,48 +1195,50 @@ class Query(BaseExpression):
1151
1195
  if summarize:
1152
1196
  expression = Ref(annotation, expression)
1153
1197
  return expression_lookups, (), expression
1154
- _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())
1198
+ _, field, _, lookup_parts = self.names_to_path(
1199
+ lookup_splitted, self.get_model_meta()
1200
+ )
1155
1201
  field_parts = lookup_splitted[0 : len(lookup_splitted) - len(lookup_parts)]
1156
1202
  if len(lookup_parts) > 1 and not field_parts:
1157
1203
  raise FieldError(
1158
- f'Invalid lookup "{lookup}" for model {self.get_meta().model.__name__}".'
1204
+ f'Invalid lookup "{lookup}" for model {self.get_model_meta().model.__name__}".'
1159
1205
  )
1160
- return lookup_parts, field_parts, False
1206
+ return lookup_parts, field_parts, False # type: ignore[return-value]
1161
1207
 
1162
- def check_query_object_type(self, value, opts, field):
1208
+ def check_query_object_type(self, value: Any, meta: Meta, field: Field) -> None:
1163
1209
  """
1164
1210
  Check whether the object passed while querying is of the correct type.
1165
1211
  If not, raise a ValueError specifying the wrong object.
1166
1212
  """
1167
- if hasattr(value, "_meta"):
1168
- if not check_rel_lookup_compatibility(value._meta.model, opts, field):
1213
+ if hasattr(value, "_model_meta"):
1214
+ if not check_rel_lookup_compatibility(value._model_meta.model, meta, field):
1169
1215
  raise ValueError(
1170
- f'Cannot query "{value}": Must be "{opts.object_name}" instance.'
1216
+ f'Cannot query "{value}": Must be "{meta.model.model_options.object_name}" instance.'
1171
1217
  )
1172
1218
 
1173
- def check_related_objects(self, field, value, opts):
1219
+ def check_related_objects(self, field: Field, value: Any, meta: Meta) -> None:
1174
1220
  """Check the type of object passed to query relations."""
1175
1221
  if field.is_relation:
1176
1222
  # Check that the field and the queryset use the same model in a
1177
1223
  # query like .filter(author=Author.query.all()). For example, the
1178
- # opts would be Author's (from the author field) and value.model
1224
+ # meta would be Author's (from the author field) and value.model
1179
1225
  # would be Author.query.all() queryset's .model (Author also).
1180
1226
  # The field is the related field on the lhs side.
1181
1227
  if (
1182
1228
  isinstance(value, Query)
1183
1229
  and not value.has_select_fields
1184
- and not check_rel_lookup_compatibility(value.model, opts, field)
1230
+ and not check_rel_lookup_compatibility(value.model, meta, field)
1185
1231
  ):
1186
1232
  raise ValueError(
1187
- f'Cannot use QuerySet for "{value.model._meta.object_name}": Use a QuerySet for "{opts.object_name}".'
1233
+ f'Cannot use QuerySet for "{value.model.model_options.object_name}": Use a QuerySet for "{meta.model.model_options.object_name}".'
1188
1234
  )
1189
- elif hasattr(value, "_meta"):
1190
- self.check_query_object_type(value, opts, field)
1235
+ elif hasattr(value, "_model_meta"):
1236
+ self.check_query_object_type(value, meta, field)
1191
1237
  elif hasattr(value, "__iter__"):
1192
1238
  for v in value:
1193
- self.check_query_object_type(v, opts, field)
1239
+ self.check_query_object_type(v, meta, field)
1194
1240
 
1195
- def check_filterable(self, expression):
1241
+ def check_filterable(self, expression: Any) -> None:
1196
1242
  """Raise an error if expression cannot be used in a WHERE clause."""
1197
1243
  if hasattr(expression, "resolve_expression") and not getattr(
1198
1244
  expression, "filterable", True
@@ -1204,7 +1250,9 @@ class Query(BaseExpression):
1204
1250
  for expr in expression.get_source_expressions():
1205
1251
  self.check_filterable(expr)
1206
1252
 
1207
- def build_lookup(self, lookups, lhs, rhs):
1253
+ def build_lookup(
1254
+ self, lookups: list[str], lhs: BaseExpression, rhs: Any
1255
+ ) -> Lookup | None:
1208
1256
  """
1209
1257
  Try to extract transforms and lookup from given lhs.
1210
1258
 
@@ -1235,11 +1283,11 @@ class Query(BaseExpression):
1235
1283
  if lookup.rhs is None and not lookup.can_use_none_as_rhs:
1236
1284
  if lookup_name not in ("exact", "iexact"):
1237
1285
  raise ValueError("Cannot use None as a query value")
1238
- return lhs.get_lookup("isnull")(lhs, True)
1286
+ return lhs.get_lookup("isnull")(lhs, True) # type: ignore[return-value]
1239
1287
 
1240
1288
  return lookup
1241
1289
 
1242
- def try_transform(self, lhs, name):
1290
+ def try_transform(self, lhs: BaseExpression, name: str) -> BaseExpression:
1243
1291
  """
1244
1292
  Helper method for build_lookup(). Try to fetch and initialize
1245
1293
  a transform for name parameter from lhs.
@@ -1265,16 +1313,16 @@ class Query(BaseExpression):
1265
1313
 
1266
1314
  def build_filter(
1267
1315
  self,
1268
- filter_expr,
1269
- branch_negated=False,
1270
- current_negated=False,
1271
- can_reuse=None,
1272
- allow_joins=True,
1273
- split_subq=True,
1274
- reuse_with_filtered_relation=False,
1275
- check_filterable=True,
1276
- summarize=False,
1277
- ):
1316
+ filter_expr: tuple[str, Any] | Q | BaseExpression,
1317
+ branch_negated: bool = False,
1318
+ current_negated: bool = False,
1319
+ can_reuse: set[str] | None = None,
1320
+ allow_joins: bool = True,
1321
+ split_subq: bool = True,
1322
+ reuse_with_filtered_relation: bool = False,
1323
+ check_filterable: bool = True,
1324
+ summarize: bool = False,
1325
+ ) -> tuple[WhereNode, set[str] | tuple[()]]:
1278
1326
  """
1279
1327
  Build a WhereNode for a single filter clause but don't add it
1280
1328
  to this Query. Query.add_q() will then add this filter to the where
@@ -1321,10 +1369,10 @@ class Query(BaseExpression):
1321
1369
  raise TypeError("Cannot filter against a non-conditional expression.")
1322
1370
  condition = filter_expr.resolve_expression(
1323
1371
  self, allow_joins=allow_joins, summarize=summarize
1324
- )
1372
+ ) # type: ignore[attr-defined]
1325
1373
  if not isinstance(condition, Lookup):
1326
1374
  condition = self.build_lookup(["exact"], condition, True)
1327
- return WhereNode([condition], connector=AND), []
1375
+ return WhereNode([condition], connector=AND), [] # type: ignore[return-value]
1328
1376
  arg, value = filter_expr
1329
1377
  if not arg:
1330
1378
  raise FieldError(f"Cannot parse keyword query {arg!r}")
@@ -1347,16 +1395,16 @@ class Query(BaseExpression):
1347
1395
 
1348
1396
  if reffed_expression:
1349
1397
  condition = self.build_lookup(lookups, reffed_expression, value)
1350
- return WhereNode([condition], connector=AND), []
1398
+ return WhereNode([condition], connector=AND), [] # type: ignore[return-value]
1351
1399
 
1352
- opts = self.get_meta()
1400
+ meta = self.get_model_meta()
1353
1401
  alias = self.get_initial_alias()
1354
1402
  allow_many = not branch_negated or not split_subq
1355
1403
 
1356
1404
  try:
1357
1405
  join_info = self.setup_joins(
1358
1406
  parts,
1359
- opts,
1407
+ meta,
1360
1408
  alias,
1361
1409
  can_reuse=can_reuse,
1362
1410
  allow_many=allow_many,
@@ -1366,7 +1414,7 @@ class Query(BaseExpression):
1366
1414
  # Prevent iterator from being consumed by check_related_objects()
1367
1415
  if isinstance(value, Iterator):
1368
1416
  value = list(value)
1369
- self.check_related_objects(join_info.final_field, value, join_info.opts)
1417
+ self.check_related_objects(join_info.final_field, value, join_info.meta)
1370
1418
 
1371
1419
  # split_exclude() needs to know which joins were generated for the
1372
1420
  # lookup parts
@@ -1430,10 +1478,10 @@ class Query(BaseExpression):
1430
1478
  clause.add(lookup_class(value, False), AND)
1431
1479
  return clause, used_joins if not require_outer else ()
1432
1480
 
1433
- def add_filter(self, filter_lhs, filter_rhs):
1481
+ def add_filter(self, filter_lhs: str, filter_rhs: Any) -> None:
1434
1482
  self.add_q(Q((filter_lhs, filter_rhs)))
1435
1483
 
1436
- def add_q(self, q_object):
1484
+ def add_q(self, q_object: Q) -> None: # type: ignore[unsupported-operator]
1437
1485
  """
1438
1486
  A preprocessor for the internal _add_q(). Responsible for doing final
1439
1487
  join promotion.
@@ -1452,23 +1500,23 @@ class Query(BaseExpression):
1452
1500
  self.where.add(clause, AND)
1453
1501
  self.demote_joins(existing_inner)
1454
1502
 
1455
- def build_where(self, filter_expr):
1503
+ def build_where(self, filter_expr: tuple[str, Any]) -> WhereNode:
1456
1504
  return self.build_filter(filter_expr, allow_joins=False)[0]
1457
1505
 
1458
- def clear_where(self):
1506
+ def clear_where(self) -> None:
1459
1507
  self.where = WhereNode()
1460
1508
 
1461
1509
  def _add_q(
1462
1510
  self,
1463
- q_object,
1464
- used_aliases,
1465
- branch_negated=False,
1466
- current_negated=False,
1467
- allow_joins=True,
1468
- split_subq=True,
1469
- check_filterable=True,
1470
- summarize=False,
1471
- ):
1511
+ q_object: Q,
1512
+ used_aliases: set[str] | None,
1513
+ branch_negated: bool = False,
1514
+ current_negated: bool = False,
1515
+ allow_joins: bool = True,
1516
+ split_subq: bool = True,
1517
+ check_filterable: bool = True,
1518
+ summarize: bool = False,
1519
+ ) -> tuple[WhereNode, set[str] | tuple[()]]: # type: ignore[unsupported-operator]
1472
1520
  """Add a Q-object to the current filter."""
1473
1521
  connector = q_object.connector
1474
1522
  current_negated ^= q_object.negated
@@ -1495,8 +1543,12 @@ class Query(BaseExpression):
1495
1543
  return target_clause, needed_inner
1496
1544
 
1497
1545
  def build_filtered_relation_q(
1498
- self, q_object, reuse, branch_negated=False, current_negated=False
1499
- ):
1546
+ self,
1547
+ q_object: Q,
1548
+ reuse: set[str],
1549
+ branch_negated: bool = False,
1550
+ current_negated: bool = False,
1551
+ ) -> WhereNode: # type: ignore[unsupported-operator]
1500
1552
  """Add a FilteredRelation object to the current filter."""
1501
1553
  connector = q_object.connector
1502
1554
  current_negated ^= q_object.negated
@@ -1523,7 +1575,7 @@ class Query(BaseExpression):
1523
1575
  target_clause.add(child_clause, connector)
1524
1576
  return target_clause
1525
1577
 
1526
- def add_filtered_relation(self, filtered_relation, alias):
1578
+ def add_filtered_relation(self, filtered_relation: Any, alias: str) -> None:
1527
1579
  filtered_relation.alias = alias
1528
1580
  lookups = dict(get_children_from_q(filtered_relation.condition))
1529
1581
  relation_lookup_parts, relation_field_parts, _ = self.solve_lookup_type(
@@ -1553,12 +1605,18 @@ class Query(BaseExpression):
1553
1605
  )
1554
1606
  self._filtered_relations[filtered_relation.alias] = filtered_relation
1555
1607
 
1556
- def names_to_path(self, names, opts, allow_many=True, fail_on_missing=False):
1608
+ def names_to_path(
1609
+ self,
1610
+ names: list[str],
1611
+ meta: Meta,
1612
+ allow_many: bool = True,
1613
+ fail_on_missing: bool = False,
1614
+ ) -> tuple[list[Any], Field, tuple[Field, ...], list[str]]:
1557
1615
  """
1558
1616
  Walk the list of names and turns them into PathInfo tuples. A single
1559
1617
  name in 'names' can generate multiple PathInfos (m2m, for example).
1560
1618
 
1561
- 'names' is the path of names to travel, 'opts' is the model Options we
1619
+ 'names' is the path of names to travel, 'meta' is the Meta we
1562
1620
  start the name resolving from, 'allow_many' is as for setup_joins().
1563
1621
  If fail_on_missing is set to True, then a name that can't be resolved
1564
1622
  will generate a FieldError.
@@ -1575,9 +1633,9 @@ class Query(BaseExpression):
1575
1633
  field = None
1576
1634
  filtered_relation = None
1577
1635
  try:
1578
- if opts is None:
1636
+ if meta is None:
1579
1637
  raise FieldDoesNotExist
1580
- field = opts.get_field(name)
1638
+ field = meta.get_field(name)
1581
1639
  except FieldDoesNotExist:
1582
1640
  if name in self.annotation_select:
1583
1641
  field = self.annotation_select[name].output_field
@@ -1587,13 +1645,13 @@ class Query(BaseExpression):
1587
1645
  parts = filtered_relation.relation_name.split(LOOKUP_SEP)
1588
1646
  filtered_relation_path, field, _, _ = self.names_to_path(
1589
1647
  parts,
1590
- opts,
1648
+ meta,
1591
1649
  allow_many,
1592
1650
  fail_on_missing,
1593
1651
  )
1594
1652
  path.extend(filtered_relation_path[:-1])
1595
1653
  else:
1596
- field = opts.get_field(filtered_relation.relation_name)
1654
+ field = meta.get_field(filtered_relation.relation_name) # type: ignore[attr-defined]
1597
1655
  if field is not None:
1598
1656
  # Fields that contain one-to-many relations with a generic
1599
1657
  # model (like a GenericForeignKey) cannot generate reverse
@@ -1612,7 +1670,7 @@ class Query(BaseExpression):
1612
1670
  if pos == -1 or fail_on_missing:
1613
1671
  available = sorted(
1614
1672
  [
1615
- *get_field_names_from_opts(opts),
1673
+ *get_field_names_from_opts(meta),
1616
1674
  *self.annotation_select,
1617
1675
  *self._filtered_relations,
1618
1676
  ]
@@ -1637,7 +1695,7 @@ class Query(BaseExpression):
1637
1695
  last = pathinfos[-1]
1638
1696
  path.extend(pathinfos)
1639
1697
  final_field = last.join_field
1640
- opts = last.to_opts
1698
+ meta = last.to_meta
1641
1699
  targets = last.target_fields
1642
1700
  cur_names_with_path[1].extend(pathinfos)
1643
1701
  names_with_path.append(cur_names_with_path)
@@ -1655,16 +1713,16 @@ class Query(BaseExpression):
1655
1713
 
1656
1714
  def setup_joins(
1657
1715
  self,
1658
- names,
1659
- opts,
1660
- alias,
1661
- can_reuse=None,
1662
- allow_many=True,
1663
- reuse_with_filtered_relation=False,
1664
- ):
1716
+ names: list[str],
1717
+ meta: Meta,
1718
+ alias: str,
1719
+ can_reuse: set[str] | None = None,
1720
+ allow_many: bool = True,
1721
+ reuse_with_filtered_relation: bool = False,
1722
+ ) -> JoinInfo:
1665
1723
  """
1666
1724
  Compute the necessary table joins for the passage through the fields
1667
- given in 'names'. 'opts' is the Options class for the current model
1725
+ given in 'names'. 'meta' is the Meta for the current model
1668
1726
  (which gives the table we are starting from), 'alias' is the alias for
1669
1727
  the table to start the joining from.
1670
1728
 
@@ -1698,10 +1756,10 @@ class Query(BaseExpression):
1698
1756
  # directly, compute transforms here and create a partial that converts
1699
1757
  # fields to the appropriate wrapped version.
1700
1758
 
1701
- def final_transformer(field, alias):
1759
+ def final_transformer(field: Field, alias: str | None) -> Col:
1702
1760
  if not self.alias_cols:
1703
1761
  alias = None
1704
- return field.get_col(alias)
1762
+ return field.get_col(alias) # type: ignore[arg-type]
1705
1763
 
1706
1764
  # Try resolving all the names as fields first. If there's an error,
1707
1765
  # treat trailing names as lookups until a field can be resolved.
@@ -1710,7 +1768,7 @@ class Query(BaseExpression):
1710
1768
  try:
1711
1769
  path, final_field, targets, rest = self.names_to_path(
1712
1770
  names[:pivot],
1713
- opts,
1771
+ meta,
1714
1772
  allow_many,
1715
1773
  fail_on_missing=True,
1716
1774
  )
@@ -1728,7 +1786,9 @@ class Query(BaseExpression):
1728
1786
  break
1729
1787
  for name in transforms:
1730
1788
 
1731
- def transform(field, alias, *, name, previous):
1789
+ def transform(
1790
+ field: Field, alias: str | None, *, name: str, previous: Any
1791
+ ) -> BaseExpression:
1732
1792
  try:
1733
1793
  wrapped = previous(field, alias)
1734
1794
  return self.try_transform(wrapped, name)
@@ -1739,10 +1799,10 @@ class Query(BaseExpression):
1739
1799
  else:
1740
1800
  raise
1741
1801
 
1742
- final_transformer = functools.partial(
1802
+ final_transformer = functools.partial( # type: ignore[misc]
1743
1803
  transform, name=name, previous=final_transformer
1744
1804
  )
1745
- final_transformer.has_transforms = True
1805
+ final_transformer.has_transforms = True # type: ignore[attr-defined]
1746
1806
  # Then, add the path to the query's joins. Note that we can't trim
1747
1807
  # joins at this stage - we will need the information about join type
1748
1808
  # of the trimmed joins.
@@ -1753,13 +1813,13 @@ class Query(BaseExpression):
1753
1813
  else:
1754
1814
  filtered_relation = None
1755
1815
  table_alias = None
1756
- opts = join.to_opts
1816
+ meta = join.to_meta
1757
1817
  if join.direct:
1758
1818
  nullable = self.is_nullable(join.join_field)
1759
1819
  else:
1760
1820
  nullable = True
1761
1821
  connection = self.join_class(
1762
- opts.db_table,
1822
+ meta.model.model_options.db_table,
1763
1823
  alias,
1764
1824
  table_alias,
1765
1825
  INNER,
@@ -1776,9 +1836,11 @@ class Query(BaseExpression):
1776
1836
  joins.append(alias)
1777
1837
  if filtered_relation:
1778
1838
  filtered_relation.path = joins[:]
1779
- return JoinInfo(final_field, targets, opts, joins, path, final_transformer)
1839
+ return JoinInfo(final_field, targets, meta, joins, path, final_transformer)
1780
1840
 
1781
- def trim_joins(self, targets, joins, path):
1841
+ def trim_joins(
1842
+ self, targets: tuple[Field, ...], joins: list[str], path: list[Any]
1843
+ ) -> tuple[tuple[Field, ...], str, list[str]]:
1782
1844
  """
1783
1845
  The 'target' parameter is the final field being joined to, 'joins'
1784
1846
  is the full list of join aliases. The 'path' contain the PathInfos
@@ -1811,7 +1873,12 @@ class Query(BaseExpression):
1811
1873
  return targets, joins[-1], joins
1812
1874
 
1813
1875
  @classmethod
1814
- def _gen_cols(cls, exprs, include_external=False, resolve_refs=True):
1876
+ def _gen_cols(
1877
+ cls,
1878
+ exprs: TypingIterator[Any],
1879
+ include_external: bool = False,
1880
+ resolve_refs: bool = True,
1881
+ ) -> TypingIterator[Col]:
1815
1882
  for expr in exprs:
1816
1883
  if isinstance(expr, Col):
1817
1884
  yield expr
@@ -1829,10 +1896,16 @@ class Query(BaseExpression):
1829
1896
  )
1830
1897
 
1831
1898
  @classmethod
1832
- def _gen_col_aliases(cls, exprs):
1899
+ def _gen_col_aliases(cls, exprs: TypingIterator[Any]) -> TypingIterator[str]:
1833
1900
  yield from (expr.alias for expr in cls._gen_cols(exprs))
1834
1901
 
1835
- def resolve_ref(self, name, allow_joins=True, reuse=None, summarize=False):
1902
+ def resolve_ref(
1903
+ self,
1904
+ name: str,
1905
+ allow_joins: bool = True,
1906
+ reuse: set[str] | None = None,
1907
+ summarize: bool = False,
1908
+ ) -> BaseExpression:
1836
1909
  annotation = self.annotations.get(name)
1837
1910
  if annotation is not None:
1838
1911
  if not allow_joins:
@@ -1862,7 +1935,10 @@ class Query(BaseExpression):
1862
1935
  annotation = self.try_transform(annotation, transform)
1863
1936
  return annotation
1864
1937
  join_info = self.setup_joins(
1865
- field_list, self.get_meta(), self.get_initial_alias(), can_reuse=reuse
1938
+ field_list,
1939
+ self.get_model_meta(),
1940
+ self.get_initial_alias(),
1941
+ can_reuse=reuse,
1866
1942
  )
1867
1943
  targets, final_alias, join_list = self.trim_joins(
1868
1944
  join_info.targets, join_info.joins, join_info.path
@@ -1882,7 +1958,12 @@ class Query(BaseExpression):
1882
1958
  reuse.update(join_list)
1883
1959
  return transform
1884
1960
 
1885
- def split_exclude(self, filter_expr, can_reuse, names_with_path):
1961
+ def split_exclude(
1962
+ self,
1963
+ filter_expr: tuple[str, Any],
1964
+ can_reuse: set[str],
1965
+ names_with_path: list[tuple[str, list[Any]]],
1966
+ ) -> tuple[WhereNode, set[str] | tuple[()]]:
1886
1967
  """
1887
1968
  When doing an exclude against any kind of N-to-many relation, we need
1888
1969
  to use a subquery. This method constructs the nested query, given the
@@ -1919,7 +2000,7 @@ class Query(BaseExpression):
1919
2000
  select_field = col.target
1920
2001
  alias = col.alias
1921
2002
  if alias in can_reuse:
1922
- id_field = select_field.model._meta.get_field("id")
2003
+ id_field = select_field.model._model_meta.get_field("id")
1923
2004
  # Need to add a restriction so that outer query's filters are in effect for
1924
2005
  # the subquery, too.
1925
2006
  query.bump_prefix(self)
@@ -1953,15 +2034,15 @@ class Query(BaseExpression):
1953
2034
  # outercol IS NULL we will not match the row.
1954
2035
  return condition, needed_inner
1955
2036
 
1956
- def set_empty(self):
2037
+ def set_empty(self) -> None:
1957
2038
  self.where.add(NothingNode(), AND)
1958
2039
  for query in self.combined_queries:
1959
2040
  query.set_empty()
1960
2041
 
1961
- def is_empty(self):
2042
+ def is_empty(self) -> bool:
1962
2043
  return any(isinstance(c, NothingNode) for c in self.where.children)
1963
2044
 
1964
- def set_limits(self, low=None, high=None):
2045
+ def set_limits(self, low: int | None = None, high: int | None = None) -> None:
1965
2046
  """
1966
2047
  Adjust the limits on the rows retrieved. Use low/high to set these,
1967
2048
  as it makes it more Pythonic to read and write. When the SQL query is
@@ -1984,18 +2065,18 @@ class Query(BaseExpression):
1984
2065
  if self.low_mark == self.high_mark:
1985
2066
  self.set_empty()
1986
2067
 
1987
- def clear_limits(self):
2068
+ def clear_limits(self) -> None:
1988
2069
  """Clear any existing limits."""
1989
2070
  self.low_mark, self.high_mark = 0, None
1990
2071
 
1991
2072
  @property
1992
- def is_sliced(self):
2073
+ def is_sliced(self) -> bool:
1993
2074
  return self.low_mark != 0 or self.high_mark is not None
1994
2075
 
1995
- def has_limit_one(self):
2076
+ def has_limit_one(self) -> bool:
1996
2077
  return self.high_mark is not None and (self.high_mark - self.low_mark) == 1
1997
2078
 
1998
- def can_filter(self):
2079
+ def can_filter(self) -> bool:
1999
2080
  """
2000
2081
  Return True if adding filters to this instance is still possible.
2001
2082
 
@@ -2003,7 +2084,7 @@ class Query(BaseExpression):
2003
2084
  """
2004
2085
  return not self.is_sliced
2005
2086
 
2006
- def clear_select_clause(self):
2087
+ def clear_select_clause(self) -> None:
2007
2088
  """Remove all fields from SELECT clause."""
2008
2089
  self.select = ()
2009
2090
  self.default_cols = False
@@ -2011,7 +2092,7 @@ class Query(BaseExpression):
2011
2092
  self.set_extra_mask(())
2012
2093
  self.set_annotation_mask(())
2013
2094
 
2014
- def clear_select_fields(self):
2095
+ def clear_select_fields(self) -> None:
2015
2096
  """
2016
2097
  Clear the list of fields to select (but not extra_select columns).
2017
2098
  Some queryset types completely replace any existing list of select
@@ -2020,28 +2101,30 @@ class Query(BaseExpression):
2020
2101
  self.select = ()
2021
2102
  self.values_select = ()
2022
2103
 
2023
- def add_select_col(self, col, name):
2104
+ def add_select_col(self, col: Col, name: str) -> None:
2024
2105
  self.select += (col,)
2025
2106
  self.values_select += (name,)
2026
2107
 
2027
- def set_select(self, cols):
2108
+ def set_select(self, cols: list[Col] | tuple[Col, ...]) -> None:
2028
2109
  self.default_cols = False
2029
2110
  self.select = tuple(cols)
2030
2111
 
2031
- def add_distinct_fields(self, *field_names):
2112
+ def add_distinct_fields(self, *field_names: str) -> None:
2032
2113
  """
2033
2114
  Add and resolve the given fields to the query's "distinct on" clause.
2034
2115
  """
2035
2116
  self.distinct_fields = field_names
2036
2117
  self.distinct = True
2037
2118
 
2038
- def add_fields(self, field_names, allow_m2m=True):
2119
+ def add_fields(
2120
+ self, field_names: list[str] | TypingIterator[str], allow_m2m: bool = True
2121
+ ) -> None:
2039
2122
  """
2040
2123
  Add the given (model) fields to the select set. Add the field names in
2041
2124
  the order specified.
2042
2125
  """
2043
2126
  alias = self.get_initial_alias()
2044
- opts = self.get_meta()
2127
+ meta = self.get_model_meta()
2045
2128
 
2046
2129
  try:
2047
2130
  cols = []
@@ -2049,7 +2132,7 @@ class Query(BaseExpression):
2049
2132
  # Join promotion note - we must not remove any rows here, so
2050
2133
  # if there is no existing joins, use outer join.
2051
2134
  join_info = self.setup_joins(
2052
- name.split(LOOKUP_SEP), opts, alias, allow_many=allow_m2m
2135
+ name.split(LOOKUP_SEP), meta, alias, allow_many=allow_m2m
2053
2136
  )
2054
2137
  targets, final_alias, joins = self.trim_joins(
2055
2138
  join_info.targets,
@@ -2074,7 +2157,7 @@ class Query(BaseExpression):
2074
2157
  else:
2075
2158
  names = sorted(
2076
2159
  [
2077
- *get_field_names_from_opts(opts),
2160
+ *get_field_names_from_opts(meta),
2078
2161
  *self.extra,
2079
2162
  *self.annotation_select,
2080
2163
  *self._filtered_relations,
@@ -2086,7 +2169,7 @@ class Query(BaseExpression):
2086
2169
  )
2087
2170
  )
2088
2171
 
2089
- def add_ordering(self, *ordering):
2172
+ def add_ordering(self, *ordering: str | BaseExpression) -> None:
2090
2173
  """
2091
2174
  Add items from the 'ordering' sequence to the query's "order by"
2092
2175
  clause. These items are either field names (not column names) --
@@ -2107,7 +2190,7 @@ class Query(BaseExpression):
2107
2190
  continue
2108
2191
  # names_to_path() validates the lookup. A descriptive
2109
2192
  # FieldError will be raise if it's not.
2110
- self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)
2193
+ self.names_to_path(item.split(LOOKUP_SEP), self.model._model_meta)
2111
2194
  elif not hasattr(item, "resolve_expression"):
2112
2195
  errors.append(item)
2113
2196
  if getattr(item, "contains_aggregate", False):
@@ -2122,7 +2205,7 @@ class Query(BaseExpression):
2122
2205
  else:
2123
2206
  self.default_ordering = False
2124
2207
 
2125
- def clear_ordering(self, force=False, clear_default=True):
2208
+ def clear_ordering(self, force: bool = False, clear_default: bool = True) -> None:
2126
2209
  """
2127
2210
  Remove any ordering settings if the current query allows it without
2128
2211
  side effects, set 'force' to True to clear the ordering regardless.
@@ -2138,7 +2221,7 @@ class Query(BaseExpression):
2138
2221
  if clear_default:
2139
2222
  self.default_ordering = False
2140
2223
 
2141
- def set_group_by(self, allow_aliases=True):
2224
+ def set_group_by(self, allow_aliases: bool = True) -> None:
2142
2225
  """
2143
2226
  Expand the GROUP BY clause required by the query.
2144
2227
 
@@ -2171,7 +2254,7 @@ class Query(BaseExpression):
2171
2254
  group_by.extend(group_by_cols)
2172
2255
  self.group_by = tuple(group_by)
2173
2256
 
2174
- def add_select_related(self, fields):
2257
+ def add_select_related(self, fields: list[str]) -> None:
2175
2258
  """
2176
2259
  Set up the select_related data structure so that we only select
2177
2260
  certain related models (as opposed to all models, when
@@ -2187,7 +2270,15 @@ class Query(BaseExpression):
2187
2270
  d = d.setdefault(part, {})
2188
2271
  self.select_related = field_dict
2189
2272
 
2190
- def add_extra(self, select, select_params, where, params, tables, order_by):
2273
+ def add_extra(
2274
+ self,
2275
+ select: dict[str, str],
2276
+ select_params: list[Any] | None,
2277
+ where: list[str],
2278
+ params: list[Any],
2279
+ tables: list[str],
2280
+ order_by: tuple[str, ...],
2281
+ ) -> None:
2191
2282
  """
2192
2283
  Add data to the various extra_* attributes for user-created additions
2193
2284
  to the query.
@@ -2220,11 +2311,11 @@ class Query(BaseExpression):
2220
2311
  if order_by:
2221
2312
  self.extra_order_by = order_by
2222
2313
 
2223
- def clear_deferred_loading(self):
2314
+ def clear_deferred_loading(self) -> None:
2224
2315
  """Remove any fields from the deferred loading set."""
2225
2316
  self.deferred_loading = (frozenset(), True)
2226
2317
 
2227
- def add_deferred_loading(self, field_names):
2318
+ def add_deferred_loading(self, field_names: frozenset[str]) -> None:
2228
2319
  """
2229
2320
  Add the given list of model field names to the set of fields to
2230
2321
  exclude from loading from the database when automatic column selection
@@ -2249,7 +2340,7 @@ class Query(BaseExpression):
2249
2340
  if new_only := set(field_names).difference(existing):
2250
2341
  self.deferred_loading = new_only, True
2251
2342
 
2252
- def add_immediate_loading(self, field_names):
2343
+ def add_immediate_loading(self, field_names: list[str] | set[str]) -> None:
2253
2344
  """
2254
2345
  Add the given list of model field names to the set of fields to
2255
2346
  retrieve when the SQL is executed ("immediate loading" fields). The
@@ -2270,7 +2361,9 @@ class Query(BaseExpression):
2270
2361
  # Replace any existing "immediate load" field names.
2271
2362
  self.deferred_loading = frozenset(field_names), False
2272
2363
 
2273
- def set_annotation_mask(self, names):
2364
+ def set_annotation_mask(
2365
+ self, names: set[str] | frozenset[str] | list[str] | dict[str, Any] | None
2366
+ ) -> None: # type: ignore[misc]
2274
2367
  """Set the mask of annotations that will be returned by the SELECT."""
2275
2368
  if names is None:
2276
2369
  self.annotation_select_mask = None
@@ -2278,11 +2371,11 @@ class Query(BaseExpression):
2278
2371
  self.annotation_select_mask = set(names)
2279
2372
  self._annotation_select_cache = None
2280
2373
 
2281
- def append_annotation_mask(self, names):
2374
+ def append_annotation_mask(self, names: list[str] | dict[str, Any]) -> None:
2282
2375
  if self.annotation_select_mask is not None:
2283
2376
  self.set_annotation_mask(self.annotation_select_mask.union(names))
2284
2377
 
2285
- def set_extra_mask(self, names):
2378
+ def set_extra_mask(self, names: set[str] | tuple[str, ...] | None) -> None:
2286
2379
  """
2287
2380
  Set the mask of extra select items that will be returned by SELECT.
2288
2381
  Don't remove them from the Query since they might be used later.
@@ -2293,7 +2386,7 @@ class Query(BaseExpression):
2293
2386
  self.extra_select_mask = set(names)
2294
2387
  self._extra_select_cache = None
2295
2388
 
2296
- def set_values(self, fields):
2389
+ def set_values(self, fields: list[str]) -> None:
2297
2390
  self.select_related = False
2298
2391
  self.clear_deferred_loading()
2299
2392
  self.clear_select_fields()
@@ -2320,13 +2413,13 @@ class Query(BaseExpression):
2320
2413
  self.set_annotation_mask(annotation_names)
2321
2414
  selected = frozenset(field_names + extra_names + annotation_names)
2322
2415
  else:
2323
- field_names = [f.attname for f in self.model._meta.concrete_fields]
2416
+ field_names = [f.attname for f in self.model._model_meta.concrete_fields]
2324
2417
  selected = frozenset(field_names)
2325
2418
  # Selected annotations must be known before setting the GROUP BY
2326
2419
  # clause.
2327
2420
  if self.group_by is True:
2328
2421
  self.add_fields(
2329
- (f.attname for f in self.model._meta.concrete_fields), False
2422
+ (f.attname for f in self.model._model_meta.concrete_fields), False
2330
2423
  )
2331
2424
  # Disable GROUP BY aliases to avoid orphaning references to the
2332
2425
  # SELECT clause which is about to be cleared.
@@ -2346,7 +2439,7 @@ class Query(BaseExpression):
2346
2439
  self.add_fields(field_names, True)
2347
2440
 
2348
2441
  @property
2349
- def annotation_select(self):
2442
+ def annotation_select(self) -> dict[str, BaseExpression]:
2350
2443
  """
2351
2444
  Return the dictionary of aggregate columns that are not masked and
2352
2445
  should be used in the SELECT clause. Cache this result for performance.
@@ -2366,7 +2459,7 @@ class Query(BaseExpression):
2366
2459
  return self.annotations
2367
2460
 
2368
2461
  @property
2369
- def extra_select(self):
2462
+ def extra_select(self) -> dict[str, tuple[str, list[Any]]]:
2370
2463
  if self._extra_select_cache is not None:
2371
2464
  return self._extra_select_cache
2372
2465
  if not self.extra:
@@ -2379,7 +2472,9 @@ class Query(BaseExpression):
2379
2472
  else:
2380
2473
  return self.extra
2381
2474
 
2382
- def trim_start(self, names_with_path):
2475
+ def trim_start(
2476
+ self, names_with_path: list[tuple[str, list[Any]]]
2477
+ ) -> tuple[str, bool]:
2383
2478
  """
2384
2479
  Trim joins from the start of the join path. The candidates for trim
2385
2480
  are the PathInfos in names_with_path structure that are m2m joins.
@@ -2456,7 +2551,7 @@ class Query(BaseExpression):
2456
2551
  self.set_select([f.get_col(select_alias) for f in select_fields])
2457
2552
  return trimmed_prefix, contains_louter
2458
2553
 
2459
- def is_nullable(self, field):
2554
+ def is_nullable(self, field: Field) -> bool:
2460
2555
  """Check if the given field should be treated as nullable."""
2461
2556
  # QuerySet does not have knowledge of which connection is going to be
2462
2557
  # used. For the single-database setup we always reference the default
@@ -2464,7 +2559,7 @@ class Query(BaseExpression):
2464
2559
  return field.allow_null
2465
2560
 
2466
2561
 
2467
- def get_order_dir(field, default="ASC"):
2562
+ def get_order_dir(field: str, default: str = "ASC") -> tuple[str, str]:
2468
2563
  """
2469
2564
  Return the field name and direction for an order specification. For
2470
2565
  example, '-foo' is returned as ('foo', 'DESC').
@@ -2484,7 +2579,7 @@ class JoinPromoter:
2484
2579
  conditions.
2485
2580
  """
2486
2581
 
2487
- def __init__(self, connector, num_children, negated):
2582
+ def __init__(self, connector: str, num_children: int, negated: bool):
2488
2583
  self.connector = connector
2489
2584
  self.negated = negated
2490
2585
  if self.negated:
@@ -2499,20 +2594,20 @@ class JoinPromoter:
2499
2594
  # inner and/or outer joins.
2500
2595
  self.votes = Counter()
2501
2596
 
2502
- def __repr__(self):
2597
+ def __repr__(self) -> str:
2503
2598
  return (
2504
2599
  f"{self.__class__.__qualname__}(connector={self.connector!r}, "
2505
2600
  f"num_children={self.num_children!r}, negated={self.negated!r})"
2506
2601
  )
2507
2602
 
2508
- def add_votes(self, votes):
2603
+ def add_votes(self, votes: Any) -> None:
2509
2604
  """
2510
2605
  Add single vote per item to self.votes. Parameter can be any
2511
2606
  iterable.
2512
2607
  """
2513
2608
  self.votes.update(votes)
2514
2609
 
2515
- def update_join_types(self, query):
2610
+ def update_join_types(self, query: Query) -> set[str]:
2516
2611
  """
2517
2612
  Change join types so that the generated query is as efficient as
2518
2613
  possible, but still correct. So, change as many joins as possible