plain.models 0.49.1__py3-none-any.whl → 0.50.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.
- plain/models/CHANGELOG.md +23 -0
- plain/models/aggregates.py +42 -19
- plain/models/backends/base/base.py +125 -105
- plain/models/backends/base/client.py +11 -3
- plain/models/backends/base/creation.py +22 -12
- plain/models/backends/base/features.py +10 -4
- plain/models/backends/base/introspection.py +29 -16
- plain/models/backends/base/operations.py +187 -91
- plain/models/backends/base/schema.py +267 -165
- plain/models/backends/base/validation.py +12 -3
- plain/models/backends/ddl_references.py +85 -43
- plain/models/backends/mysql/base.py +29 -26
- plain/models/backends/mysql/client.py +7 -2
- plain/models/backends/mysql/compiler.py +12 -3
- plain/models/backends/mysql/creation.py +5 -2
- plain/models/backends/mysql/features.py +24 -22
- plain/models/backends/mysql/introspection.py +22 -13
- plain/models/backends/mysql/operations.py +106 -39
- plain/models/backends/mysql/schema.py +48 -24
- plain/models/backends/mysql/validation.py +13 -6
- plain/models/backends/postgresql/base.py +41 -34
- plain/models/backends/postgresql/client.py +7 -2
- plain/models/backends/postgresql/creation.py +10 -5
- plain/models/backends/postgresql/introspection.py +15 -8
- plain/models/backends/postgresql/operations.py +109 -42
- plain/models/backends/postgresql/schema.py +85 -46
- plain/models/backends/sqlite3/_functions.py +151 -115
- plain/models/backends/sqlite3/base.py +37 -23
- plain/models/backends/sqlite3/client.py +7 -1
- plain/models/backends/sqlite3/creation.py +9 -5
- plain/models/backends/sqlite3/features.py +5 -3
- plain/models/backends/sqlite3/introspection.py +32 -16
- plain/models/backends/sqlite3/operations.py +125 -42
- plain/models/backends/sqlite3/schema.py +82 -58
- plain/models/backends/utils.py +52 -29
- plain/models/backups/cli.py +8 -6
- plain/models/backups/clients.py +16 -7
- plain/models/backups/core.py +24 -13
- plain/models/base.py +113 -74
- plain/models/cli.py +94 -63
- plain/models/config.py +1 -1
- plain/models/connections.py +23 -7
- plain/models/constraints.py +65 -47
- plain/models/database_url.py +1 -1
- plain/models/db.py +6 -2
- plain/models/deletion.py +66 -43
- plain/models/entrypoints.py +1 -1
- plain/models/enums.py +22 -11
- plain/models/exceptions.py +23 -8
- plain/models/expressions.py +440 -257
- plain/models/fields/__init__.py +253 -202
- plain/models/fields/json.py +120 -54
- plain/models/fields/mixins.py +12 -8
- plain/models/fields/related.py +284 -252
- plain/models/fields/related_descriptors.py +34 -25
- plain/models/fields/related_lookups.py +23 -11
- plain/models/fields/related_managers.py +81 -47
- plain/models/fields/reverse_related.py +58 -55
- plain/models/forms.py +89 -63
- plain/models/functions/comparison.py +71 -18
- plain/models/functions/datetime.py +79 -29
- plain/models/functions/math.py +43 -10
- plain/models/functions/mixins.py +24 -7
- plain/models/functions/text.py +104 -25
- plain/models/functions/window.py +12 -6
- plain/models/indexes.py +52 -28
- plain/models/lookups.py +228 -153
- plain/models/migrations/autodetector.py +86 -43
- plain/models/migrations/exceptions.py +7 -3
- plain/models/migrations/executor.py +33 -7
- plain/models/migrations/graph.py +79 -50
- plain/models/migrations/loader.py +45 -22
- plain/models/migrations/migration.py +23 -18
- plain/models/migrations/operations/base.py +37 -19
- plain/models/migrations/operations/fields.py +89 -42
- plain/models/migrations/operations/models.py +245 -143
- plain/models/migrations/operations/special.py +82 -25
- plain/models/migrations/optimizer.py +7 -2
- plain/models/migrations/questioner.py +58 -31
- plain/models/migrations/recorder.py +18 -11
- plain/models/migrations/serializer.py +50 -39
- plain/models/migrations/state.py +220 -133
- plain/models/migrations/utils.py +29 -13
- plain/models/migrations/writer.py +17 -14
- plain/models/options.py +63 -56
- plain/models/otel.py +16 -6
- plain/models/preflight.py +35 -12
- plain/models/query.py +323 -228
- plain/models/query_utils.py +93 -58
- plain/models/registry.py +34 -16
- plain/models/sql/compiler.py +146 -97
- plain/models/sql/datastructures.py +38 -25
- plain/models/sql/query.py +255 -169
- plain/models/sql/subqueries.py +32 -21
- plain/models/sql/where.py +54 -29
- plain/models/test/pytest.py +15 -11
- plain/models/test/utils.py +4 -2
- plain/models/transaction.py +20 -7
- plain/models/utils.py +13 -5
- {plain_models-0.49.1.dist-info → plain_models-0.50.0.dist-info}/METADATA +1 -1
- plain_models-0.50.0.dist-info/RECORD +122 -0
- plain_models-0.49.1.dist-info/RECORD +0 -122
- {plain_models-0.49.1.dist-info → plain_models-0.50.0.dist-info}/WHEEL +0 -0
- {plain_models-0.49.1.dist-info → plain_models-0.50.0.dist-info}/entry_points.txt +0 -0
- {plain_models-0.49.1.dist-info → plain_models-0.50.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.options import Options
|
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,7 +67,7 @@ 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):
|
70
|
+
def get_field_names_from_opts(opts: Options | None) -> set[str]:
|
60
71
|
if opts is None:
|
61
72
|
return set()
|
62
73
|
return set(
|
@@ -66,7 +77,7 @@ def get_field_names_from_opts(opts):
|
|
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)
|
@@ -83,7 +94,7 @@ JoinInfo = namedtuple(
|
|
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,19 @@ 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
118
|
return [converter(column_meta[0]) for column_meta in self.cursor.description]
|
108
119
|
|
109
|
-
def __iter__(self):
|
120
|
+
def __iter__(self) -> TypingIterator[Any]:
|
110
121
|
# Always execute a new query for a new iterator.
|
111
122
|
# This could be optimized with a cache at the expense of RAM.
|
112
123
|
self._execute_query()
|
@@ -118,21 +129,21 @@ class RawQuery:
|
|
118
129
|
result = self.cursor
|
119
130
|
return iter(result)
|
120
131
|
|
121
|
-
def __repr__(self):
|
132
|
+
def __repr__(self) -> str:
|
122
133
|
return f"<{self.__class__.__name__}: {self}>"
|
123
134
|
|
124
135
|
@property
|
125
|
-
def params_type(self):
|
136
|
+
def params_type(self) -> type[dict] | type[tuple] | None:
|
126
137
|
if self.params is None:
|
127
138
|
return None
|
128
139
|
return dict if isinstance(self.params, Mapping) else tuple
|
129
140
|
|
130
|
-
def __str__(self):
|
141
|
+
def __str__(self) -> str:
|
131
142
|
if self.params_type is None:
|
132
143
|
return self.sql
|
133
144
|
return self.sql % self.params_type(self.params)
|
134
145
|
|
135
|
-
def _execute_query(self):
|
146
|
+
def _execute_query(self) -> None:
|
136
147
|
# Adapt parameters to the database, as much as possible considering
|
137
148
|
# that the target type isn't known. See #17755.
|
138
149
|
params_type = self.params_type
|
@@ -226,7 +237,7 @@ class Query(BaseExpression):
|
|
226
237
|
|
227
238
|
explain_info = None
|
228
239
|
|
229
|
-
def __init__(self, model, alias_cols=True):
|
240
|
+
def __init__(self, model: type[Model], alias_cols: bool = True):
|
230
241
|
self.model = model
|
231
242
|
self.alias_refcount = {}
|
232
243
|
# alias_map is the most important data structure regarding joins.
|
@@ -255,7 +266,7 @@ class Query(BaseExpression):
|
|
255
266
|
self._filtered_relations = {}
|
256
267
|
|
257
268
|
@property
|
258
|
-
def output_field(self):
|
269
|
+
def output_field(self) -> Field | None: # type: ignore[return]
|
259
270
|
if len(self.select) == 1:
|
260
271
|
select = self.select[0]
|
261
272
|
return getattr(select, "target", None) or select.field
|
@@ -263,11 +274,11 @@ class Query(BaseExpression):
|
|
263
274
|
return next(iter(self.annotation_select.values())).output_field
|
264
275
|
|
265
276
|
@cached_property
|
266
|
-
def base_table(self):
|
277
|
+
def base_table(self) -> str | None:
|
267
278
|
for alias in self.alias_map:
|
268
279
|
return alias
|
269
280
|
|
270
|
-
def __str__(self):
|
281
|
+
def __str__(self) -> str:
|
271
282
|
"""
|
272
283
|
Return the query as a string of SQL with the parameter values
|
273
284
|
substituted in (use sql_with_params() to see the unsubstituted string).
|
@@ -278,53 +289,53 @@ class Query(BaseExpression):
|
|
278
289
|
sql, params = self.sql_with_params()
|
279
290
|
return sql % params
|
280
291
|
|
281
|
-
def sql_with_params(self):
|
292
|
+
def sql_with_params(self) -> tuple[str, tuple[Any, ...]]:
|
282
293
|
"""
|
283
294
|
Return the query as an SQL string and the parameters that will be
|
284
295
|
substituted into the query.
|
285
296
|
"""
|
286
297
|
return self.get_compiler().as_sql()
|
287
298
|
|
288
|
-
def __deepcopy__(self, memo):
|
299
|
+
def __deepcopy__(self, memo: dict[int, Any]) -> Query:
|
289
300
|
"""Limit the amount of work when a Query is deepcopied."""
|
290
301
|
result = self.clone()
|
291
302
|
memo[id(self)] = result
|
292
303
|
return result
|
293
304
|
|
294
|
-
def get_compiler(self, *, elide_empty=True):
|
305
|
+
def get_compiler(self, *, elide_empty: bool = True) -> Any:
|
295
306
|
return db_connection.ops.compiler(self.compiler)(
|
296
307
|
self, db_connection, elide_empty
|
297
308
|
)
|
298
309
|
|
299
|
-
def get_meta(self):
|
310
|
+
def get_meta(self) -> Options | None:
|
300
311
|
"""
|
301
312
|
Return the Options instance (the model._meta) from which to start
|
302
313
|
processing. Normally, this is self.model._meta, but it can be changed
|
303
314
|
by subclasses.
|
304
315
|
"""
|
305
316
|
if self.model:
|
306
|
-
return self.model._meta
|
317
|
+
return self.model._meta # type: ignore[attr-defined,return-value]
|
307
318
|
|
308
|
-
def clone(self):
|
319
|
+
def clone(self) -> Query:
|
309
320
|
"""
|
310
321
|
Return a copy of the current Query. A lightweight alternative to
|
311
322
|
deepcopy().
|
312
323
|
"""
|
313
|
-
obj = Empty()
|
314
|
-
obj.__class__ = self.__class__
|
324
|
+
obj = Empty() # type: ignore[misc]
|
325
|
+
obj.__class__ = self.__class__ # type: ignore[misc]
|
315
326
|
# Copy references to everything.
|
316
|
-
obj.__dict__ = self.__dict__.copy()
|
327
|
+
obj.__dict__ = self.__dict__.copy() # type: ignore[attr-defined]
|
317
328
|
# 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()
|
329
|
+
obj.alias_refcount = self.alias_refcount.copy() # type: ignore[attr-defined]
|
330
|
+
obj.alias_map = self.alias_map.copy() # type: ignore[attr-defined]
|
331
|
+
obj.external_aliases = self.external_aliases.copy() # type: ignore[attr-defined]
|
332
|
+
obj.table_map = self.table_map.copy() # type: ignore[attr-defined]
|
333
|
+
obj.where = self.where.clone() # type: ignore[attr-defined]
|
334
|
+
obj.annotations = self.annotations.copy() # type: ignore[attr-defined]
|
324
335
|
if self.annotation_select_mask is not None:
|
325
|
-
obj.annotation_select_mask = self.annotation_select_mask.copy()
|
336
|
+
obj.annotation_select_mask = self.annotation_select_mask.copy() # type: ignore[attr-defined]
|
326
337
|
if self.combined_queries:
|
327
|
-
obj.combined_queries = tuple(
|
338
|
+
obj.combined_queries = tuple( # type: ignore[attr-defined]
|
328
339
|
[query.clone() for query in self.combined_queries]
|
329
340
|
)
|
330
341
|
# _annotation_select_cache cannot be copied, as doing so breaks the
|
@@ -332,50 +343,50 @@ class Query(BaseExpression):
|
|
332
343
|
# _annotation_select_cache point to the same underlying objects.
|
333
344
|
# It will get re-populated in the cloned queryset the next time it's
|
334
345
|
# used.
|
335
|
-
obj._annotation_select_cache = None
|
336
|
-
obj.extra = self.extra.copy()
|
346
|
+
obj._annotation_select_cache = None # type: ignore[attr-defined]
|
347
|
+
obj.extra = self.extra.copy() # type: ignore[attr-defined]
|
337
348
|
if self.extra_select_mask is not None:
|
338
|
-
obj.extra_select_mask = self.extra_select_mask.copy()
|
349
|
+
obj.extra_select_mask = self.extra_select_mask.copy() # type: ignore[attr-defined]
|
339
350
|
if self._extra_select_cache is not None:
|
340
|
-
obj._extra_select_cache = self._extra_select_cache.copy()
|
351
|
+
obj._extra_select_cache = self._extra_select_cache.copy() # type: ignore[attr-defined]
|
341
352
|
if self.select_related is not False:
|
342
353
|
# Use deepcopy because select_related stores fields in nested
|
343
354
|
# dicts.
|
344
|
-
obj.select_related = copy.deepcopy(obj.select_related)
|
355
|
+
obj.select_related = copy.deepcopy(obj.select_related) # type: ignore[attr-defined]
|
345
356
|
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()
|
357
|
+
obj.subq_aliases = self.subq_aliases.copy() # type: ignore[attr-defined]
|
358
|
+
obj.used_aliases = self.used_aliases.copy() # type: ignore[attr-defined]
|
359
|
+
obj._filtered_relations = self._filtered_relations.copy() # type: ignore[attr-defined]
|
349
360
|
# Clear the cached_property, if it exists.
|
350
361
|
obj.__dict__.pop("base_table", None)
|
351
|
-
return obj
|
362
|
+
return obj # type: ignore[return-value]
|
352
363
|
|
353
|
-
def chain(self, klass=None):
|
364
|
+
def chain(self, klass: type[Query] | None = None) -> Query:
|
354
365
|
"""
|
355
366
|
Return a copy of the current Query that's ready for another operation.
|
356
367
|
The klass argument changes the type of the Query, e.g. UpdateQuery.
|
357
368
|
"""
|
358
369
|
obj = self.clone()
|
359
370
|
if klass and obj.__class__ != klass:
|
360
|
-
obj.__class__ = klass
|
371
|
+
obj.__class__ = klass # type: ignore[misc]
|
361
372
|
if not obj.filter_is_sticky:
|
362
|
-
obj.used_aliases = set()
|
373
|
+
obj.used_aliases = set() # type: ignore[attr-defined]
|
363
374
|
obj.filter_is_sticky = False
|
364
375
|
if hasattr(obj, "_setup_query"):
|
365
|
-
obj._setup_query()
|
366
|
-
return obj
|
376
|
+
obj._setup_query() # type: ignore[attr-defined]
|
377
|
+
return obj # type: ignore[return-value]
|
367
378
|
|
368
|
-
def relabeled_clone(self, change_map):
|
379
|
+
def relabeled_clone(self, change_map: dict[str, str]) -> Query:
|
369
380
|
clone = self.clone()
|
370
381
|
clone.change_aliases(change_map)
|
371
382
|
return clone
|
372
383
|
|
373
|
-
def _get_col(self, target, field, alias):
|
384
|
+
def _get_col(self, target: Any, field: Field, alias: str | None) -> Col:
|
374
385
|
if not self.alias_cols:
|
375
386
|
alias = None
|
376
387
|
return target.get_col(alias, field)
|
377
388
|
|
378
|
-
def get_aggregation(self, aggregate_exprs):
|
389
|
+
def get_aggregation(self, aggregate_exprs: dict[str, Any]) -> dict[str, Any]:
|
379
390
|
"""
|
380
391
|
Return the dictionary with the values of the existing aggregations.
|
381
392
|
"""
|
@@ -526,17 +537,17 @@ class Query(BaseExpression):
|
|
526
537
|
|
527
538
|
return dict(zip(outer_query.annotation_select, result))
|
528
539
|
|
529
|
-
def get_count(self):
|
540
|
+
def get_count(self) -> int:
|
530
541
|
"""
|
531
542
|
Perform a COUNT() query using the current filter constraints.
|
532
543
|
"""
|
533
544
|
obj = self.clone()
|
534
545
|
return obj.get_aggregation({"__count": Count("*")})["__count"]
|
535
546
|
|
536
|
-
def has_filters(self):
|
547
|
+
def has_filters(self) -> bool:
|
537
548
|
return self.where
|
538
549
|
|
539
|
-
def exists(self, limit=True):
|
550
|
+
def exists(self, limit: bool = True) -> Query:
|
540
551
|
q = self.clone()
|
541
552
|
if not (q.distinct and q.is_sliced):
|
542
553
|
if q.group_by is True:
|
@@ -558,12 +569,12 @@ class Query(BaseExpression):
|
|
558
569
|
q.add_annotation(Value(1), "a")
|
559
570
|
return q
|
560
571
|
|
561
|
-
def has_results(self):
|
572
|
+
def has_results(self) -> bool:
|
562
573
|
q = self.exists()
|
563
574
|
compiler = q.get_compiler()
|
564
575
|
return compiler.has_results()
|
565
576
|
|
566
|
-
def explain(self, format=None, **options):
|
577
|
+
def explain(self, format: str | None = None, **options: Any) -> str:
|
567
578
|
q = self.clone()
|
568
579
|
for option_name in options:
|
569
580
|
if (
|
@@ -575,7 +586,7 @@ class Query(BaseExpression):
|
|
575
586
|
compiler = q.get_compiler()
|
576
587
|
return "\n".join(compiler.explain_query())
|
577
588
|
|
578
|
-
def combine(self, rhs, connector):
|
589
|
+
def combine(self, rhs: Query, connector: str) -> None:
|
579
590
|
"""
|
580
591
|
Merge the 'rhs' query into the current one (with any 'rhs' effects
|
581
592
|
being applied *after* (that is, "to the right of") anything in the
|
@@ -690,7 +701,12 @@ class Query(BaseExpression):
|
|
690
701
|
self.order_by = rhs.order_by or self.order_by
|
691
702
|
self.extra_order_by = rhs.extra_order_by or self.extra_order_by
|
692
703
|
|
693
|
-
def _get_defer_select_mask(
|
704
|
+
def _get_defer_select_mask(
|
705
|
+
self,
|
706
|
+
opts: Options,
|
707
|
+
mask: dict[str, Any],
|
708
|
+
select_mask: dict[Any, Any] | None = None,
|
709
|
+
) -> dict[Any, Any]:
|
694
710
|
if select_mask is None:
|
695
711
|
select_mask = {}
|
696
712
|
select_mask[opts.get_field("id")] = {}
|
@@ -727,7 +743,12 @@ class Query(BaseExpression):
|
|
727
743
|
)
|
728
744
|
return select_mask
|
729
745
|
|
730
|
-
def _get_only_select_mask(
|
746
|
+
def _get_only_select_mask(
|
747
|
+
self,
|
748
|
+
opts: Options,
|
749
|
+
mask: dict[str, Any],
|
750
|
+
select_mask: dict[Any, Any] | None = None,
|
751
|
+
) -> dict[Any, Any]:
|
731
752
|
if select_mask is None:
|
732
753
|
select_mask = {}
|
733
754
|
select_mask[opts.get_field("id")] = {}
|
@@ -744,7 +765,7 @@ class Query(BaseExpression):
|
|
744
765
|
)
|
745
766
|
return select_mask
|
746
767
|
|
747
|
-
def get_select_mask(self):
|
768
|
+
def get_select_mask(self) -> dict[Any, Any]:
|
748
769
|
"""
|
749
770
|
Convert the self.deferred_loading data structure to an alternate data
|
750
771
|
structure, describing the field that *will* be loaded. This is used to
|
@@ -766,7 +787,9 @@ class Query(BaseExpression):
|
|
766
787
|
return self._get_defer_select_mask(opts, mask)
|
767
788
|
return self._get_only_select_mask(opts, mask)
|
768
789
|
|
769
|
-
def table_alias(
|
790
|
+
def table_alias(
|
791
|
+
self, table_name: str, create: bool = False, filtered_relation: Any = None
|
792
|
+
) -> tuple[str, bool]:
|
770
793
|
"""
|
771
794
|
Return a table alias for the given table_name and whether this is a
|
772
795
|
new alias or not.
|
@@ -793,15 +816,15 @@ class Query(BaseExpression):
|
|
793
816
|
self.alias_refcount[alias] = 1
|
794
817
|
return alias, True
|
795
818
|
|
796
|
-
def ref_alias(self, alias):
|
819
|
+
def ref_alias(self, alias: str) -> None:
|
797
820
|
"""Increases the reference count for this alias."""
|
798
821
|
self.alias_refcount[alias] += 1
|
799
822
|
|
800
|
-
def unref_alias(self, alias, amount=1):
|
823
|
+
def unref_alias(self, alias: str, amount: int = 1) -> None:
|
801
824
|
"""Decreases the reference count for this alias."""
|
802
825
|
self.alias_refcount[alias] -= amount
|
803
826
|
|
804
|
-
def promote_joins(self, aliases):
|
827
|
+
def promote_joins(self, aliases: set[str] | list[str]) -> None:
|
805
828
|
"""
|
806
829
|
Promote recursively the join type of given aliases and its children to
|
807
830
|
an outer join. If 'unconditional' is False, only promote the join if
|
@@ -838,7 +861,7 @@ class Query(BaseExpression):
|
|
838
861
|
and join not in aliases
|
839
862
|
)
|
840
863
|
|
841
|
-
def demote_joins(self, aliases):
|
864
|
+
def demote_joins(self, aliases: set[str] | list[str]) -> None:
|
842
865
|
"""
|
843
866
|
Change join type from LOUTER to INNER for all joins in aliases.
|
844
867
|
|
@@ -857,7 +880,7 @@ class Query(BaseExpression):
|
|
857
880
|
if self.alias_map[parent_alias].join_type == INNER:
|
858
881
|
aliases.append(parent_alias)
|
859
882
|
|
860
|
-
def reset_refcounts(self, to_counts):
|
883
|
+
def reset_refcounts(self, to_counts: dict[str, int]) -> None:
|
861
884
|
"""
|
862
885
|
Reset reference counts for aliases so that they match the value passed
|
863
886
|
in `to_counts`.
|
@@ -866,7 +889,7 @@ class Query(BaseExpression):
|
|
866
889
|
unref_amount = cur_refcount - to_counts.get(alias, 0)
|
867
890
|
self.unref_alias(alias, unref_amount)
|
868
891
|
|
869
|
-
def change_aliases(self, change_map):
|
892
|
+
def change_aliases(self, change_map: dict[str, str]) -> None:
|
870
893
|
"""
|
871
894
|
Change the aliases in change_map (which maps old-alias -> new-alias),
|
872
895
|
relabelling any references to them in select columns and the where
|
@@ -911,7 +934,9 @@ class Query(BaseExpression):
|
|
911
934
|
for alias, aliased in self.external_aliases.items()
|
912
935
|
}
|
913
936
|
|
914
|
-
def bump_prefix(
|
937
|
+
def bump_prefix(
|
938
|
+
self, other_query: Query, exclude: set[str] | dict[str, str] | None = None
|
939
|
+
) -> None:
|
915
940
|
"""
|
916
941
|
Change the alias prefix to the next letter in the alphabet in a way
|
917
942
|
that the other query's aliases and this query's aliases will not
|
@@ -919,7 +944,7 @@ class Query(BaseExpression):
|
|
919
944
|
after this call. To prevent changing aliases use the exclude parameter.
|
920
945
|
"""
|
921
946
|
|
922
|
-
def prefix_gen():
|
947
|
+
def prefix_gen() -> TypingIterator[str]:
|
923
948
|
"""
|
924
949
|
Generate a sequence of characters in alphabetical order:
|
925
950
|
-> 'A', 'B', 'C', ...
|
@@ -966,7 +991,7 @@ class Query(BaseExpression):
|
|
966
991
|
}
|
967
992
|
)
|
968
993
|
|
969
|
-
def get_initial_alias(self):
|
994
|
+
def get_initial_alias(self) -> str | None:
|
970
995
|
"""
|
971
996
|
Return the first alias for this query, after increasing its reference
|
972
997
|
count.
|
@@ -975,12 +1000,12 @@ class Query(BaseExpression):
|
|
975
1000
|
alias = self.base_table
|
976
1001
|
self.ref_alias(alias)
|
977
1002
|
elif self.model:
|
978
|
-
alias = self.join(self.base_table_class(self.get_meta().db_table, None))
|
1003
|
+
alias = self.join(self.base_table_class(self.get_meta().db_table, None)) # type: ignore[arg-type]
|
979
1004
|
else:
|
980
1005
|
alias = None
|
981
1006
|
return alias
|
982
1007
|
|
983
|
-
def count_active_tables(self):
|
1008
|
+
def count_active_tables(self) -> int:
|
984
1009
|
"""
|
985
1010
|
Return the number of tables in this query with a non-zero reference
|
986
1011
|
count. After execution, the reference counts are zeroed, so tables
|
@@ -988,7 +1013,12 @@ class Query(BaseExpression):
|
|
988
1013
|
"""
|
989
1014
|
return len([1 for count in self.alias_refcount.values() if count])
|
990
1015
|
|
991
|
-
def join(
|
1016
|
+
def join(
|
1017
|
+
self,
|
1018
|
+
join: BaseTable | Join,
|
1019
|
+
reuse: set[str] | None = None,
|
1020
|
+
reuse_with_filtered_relation: bool = False,
|
1021
|
+
) -> str:
|
992
1022
|
"""
|
993
1023
|
Return an alias for the 'join', either reusing an existing alias for
|
994
1024
|
that join or creating a new one. 'join' is either a base_table_class or
|
@@ -1029,7 +1059,7 @@ class Query(BaseExpression):
|
|
1029
1059
|
join.table_name, create=True, filtered_relation=join.filtered_relation
|
1030
1060
|
)
|
1031
1061
|
if join.join_type:
|
1032
|
-
if self.alias_map[join.parent_alias].join_type == LOUTER or join.nullable:
|
1062
|
+
if self.alias_map[join.parent_alias].join_type == LOUTER or join.nullable: # type: ignore[attr-defined]
|
1033
1063
|
join_type = LOUTER
|
1034
1064
|
else:
|
1035
1065
|
join_type = INNER
|
@@ -1038,14 +1068,16 @@ class Query(BaseExpression):
|
|
1038
1068
|
self.alias_map[alias] = join
|
1039
1069
|
return alias
|
1040
1070
|
|
1041
|
-
def check_alias(self, alias):
|
1071
|
+
def check_alias(self, alias: str) -> None:
|
1042
1072
|
if FORBIDDEN_ALIAS_PATTERN.search(alias):
|
1043
1073
|
raise ValueError(
|
1044
1074
|
"Column aliases cannot contain whitespace characters, quotation marks, "
|
1045
1075
|
"semicolons, or SQL comments."
|
1046
1076
|
)
|
1047
1077
|
|
1048
|
-
def add_annotation(
|
1078
|
+
def add_annotation(
|
1079
|
+
self, annotation: BaseExpression, alias: str, select: bool = True
|
1080
|
+
) -> None:
|
1049
1081
|
"""Add a single annotation expression to the Query."""
|
1050
1082
|
self.check_alias(alias)
|
1051
1083
|
annotation = annotation.resolve_expression(self, allow_joins=True, reuse=None)
|
@@ -1055,7 +1087,7 @@ class Query(BaseExpression):
|
|
1055
1087
|
self.set_annotation_mask(set(self.annotation_select).difference({alias}))
|
1056
1088
|
self.annotations[alias] = annotation
|
1057
1089
|
|
1058
|
-
def resolve_expression(self, query, *args, **kwargs):
|
1090
|
+
def resolve_expression(self, query: Query, *args: Any, **kwargs: Any) -> Query:
|
1059
1091
|
clone = self.clone()
|
1060
1092
|
# Subqueries need to use a different set of aliases than the outer query.
|
1061
1093
|
clone.bump_prefix(query)
|
@@ -1084,7 +1116,7 @@ class Query(BaseExpression):
|
|
1084
1116
|
)
|
1085
1117
|
return clone
|
1086
1118
|
|
1087
|
-
def get_external_cols(self):
|
1119
|
+
def get_external_cols(self) -> list[Col]:
|
1088
1120
|
exprs = chain(self.annotations.values(), self.where.children)
|
1089
1121
|
return [
|
1090
1122
|
col
|
@@ -1092,7 +1124,9 @@ class Query(BaseExpression):
|
|
1092
1124
|
if col.alias in self.external_aliases
|
1093
1125
|
]
|
1094
1126
|
|
1095
|
-
def get_group_by_cols(
|
1127
|
+
def get_group_by_cols(
|
1128
|
+
self, wrapper: BaseExpression | None = None
|
1129
|
+
) -> list[Col] | list[BaseExpression]:
|
1096
1130
|
# If wrapper is referenced by an alias for an explicit GROUP BY through
|
1097
1131
|
# values() a reference to this expression and not the self must be
|
1098
1132
|
# returned to ensure external column references are not grouped against
|
@@ -1102,7 +1136,9 @@ class Query(BaseExpression):
|
|
1102
1136
|
return [wrapper or self]
|
1103
1137
|
return external_cols
|
1104
1138
|
|
1105
|
-
def as_sql(
|
1139
|
+
def as_sql(
|
1140
|
+
self, compiler: SQLCompiler, connection: BaseDatabaseWrapper
|
1141
|
+
) -> tuple[str, tuple[Any, ...]]:
|
1106
1142
|
# Some backends (e.g. Oracle) raise an error when a subquery contains
|
1107
1143
|
# unnecessary ORDER BY clause.
|
1108
1144
|
if (
|
@@ -1117,7 +1153,9 @@ class Query(BaseExpression):
|
|
1117
1153
|
sql = f"({sql})"
|
1118
1154
|
return sql, params
|
1119
1155
|
|
1120
|
-
def resolve_lookup_value(
|
1156
|
+
def resolve_lookup_value(
|
1157
|
+
self, value: Any, can_reuse: set[str] | None, allow_joins: bool
|
1158
|
+
) -> Any:
|
1121
1159
|
if hasattr(value, "resolve_expression"):
|
1122
1160
|
value = value.resolve_expression(
|
1123
1161
|
self,
|
@@ -1137,7 +1175,9 @@ class Query(BaseExpression):
|
|
1137
1175
|
return type_(values)
|
1138
1176
|
return value
|
1139
1177
|
|
1140
|
-
def solve_lookup_type(
|
1178
|
+
def solve_lookup_type(
|
1179
|
+
self, lookup: str, summarize: bool = False
|
1180
|
+
) -> tuple[list[str] | tuple[str, ...], tuple[str, ...], BaseExpression | bool]:
|
1141
1181
|
"""
|
1142
1182
|
Solve the lookup type from the lookup (e.g.: 'foobar__id__icontains').
|
1143
1183
|
"""
|
@@ -1157,9 +1197,9 @@ class Query(BaseExpression):
|
|
1157
1197
|
raise FieldError(
|
1158
1198
|
f'Invalid lookup "{lookup}" for model {self.get_meta().model.__name__}".'
|
1159
1199
|
)
|
1160
|
-
return lookup_parts, field_parts, False
|
1200
|
+
return lookup_parts, field_parts, False # type: ignore[return-value]
|
1161
1201
|
|
1162
|
-
def check_query_object_type(self, value, opts, field):
|
1202
|
+
def check_query_object_type(self, value: Any, opts: Options, field: Field) -> None:
|
1163
1203
|
"""
|
1164
1204
|
Check whether the object passed while querying is of the correct type.
|
1165
1205
|
If not, raise a ValueError specifying the wrong object.
|
@@ -1170,7 +1210,7 @@ class Query(BaseExpression):
|
|
1170
1210
|
f'Cannot query "{value}": Must be "{opts.object_name}" instance.'
|
1171
1211
|
)
|
1172
1212
|
|
1173
|
-
def check_related_objects(self, field, value, opts):
|
1213
|
+
def check_related_objects(self, field: Field, value: Any, opts: Options) -> None:
|
1174
1214
|
"""Check the type of object passed to query relations."""
|
1175
1215
|
if field.is_relation:
|
1176
1216
|
# Check that the field and the queryset use the same model in a
|
@@ -1192,7 +1232,7 @@ class Query(BaseExpression):
|
|
1192
1232
|
for v in value:
|
1193
1233
|
self.check_query_object_type(v, opts, field)
|
1194
1234
|
|
1195
|
-
def check_filterable(self, expression):
|
1235
|
+
def check_filterable(self, expression: Any) -> None:
|
1196
1236
|
"""Raise an error if expression cannot be used in a WHERE clause."""
|
1197
1237
|
if hasattr(expression, "resolve_expression") and not getattr(
|
1198
1238
|
expression, "filterable", True
|
@@ -1204,7 +1244,9 @@ class Query(BaseExpression):
|
|
1204
1244
|
for expr in expression.get_source_expressions():
|
1205
1245
|
self.check_filterable(expr)
|
1206
1246
|
|
1207
|
-
def build_lookup(
|
1247
|
+
def build_lookup(
|
1248
|
+
self, lookups: list[str], lhs: BaseExpression, rhs: Any
|
1249
|
+
) -> Lookup | None:
|
1208
1250
|
"""
|
1209
1251
|
Try to extract transforms and lookup from given lhs.
|
1210
1252
|
|
@@ -1235,11 +1277,11 @@ class Query(BaseExpression):
|
|
1235
1277
|
if lookup.rhs is None and not lookup.can_use_none_as_rhs:
|
1236
1278
|
if lookup_name not in ("exact", "iexact"):
|
1237
1279
|
raise ValueError("Cannot use None as a query value")
|
1238
|
-
return lhs.get_lookup("isnull")(lhs, True)
|
1280
|
+
return lhs.get_lookup("isnull")(lhs, True) # type: ignore[return-value]
|
1239
1281
|
|
1240
1282
|
return lookup
|
1241
1283
|
|
1242
|
-
def try_transform(self, lhs, name):
|
1284
|
+
def try_transform(self, lhs: BaseExpression, name: str) -> BaseExpression:
|
1243
1285
|
"""
|
1244
1286
|
Helper method for build_lookup(). Try to fetch and initialize
|
1245
1287
|
a transform for name parameter from lhs.
|
@@ -1265,16 +1307,16 @@ class Query(BaseExpression):
|
|
1265
1307
|
|
1266
1308
|
def build_filter(
|
1267
1309
|
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
|
-
):
|
1310
|
+
filter_expr: tuple[str, Any] | Q | BaseExpression,
|
1311
|
+
branch_negated: bool = False,
|
1312
|
+
current_negated: bool = False,
|
1313
|
+
can_reuse: set[str] | None = None,
|
1314
|
+
allow_joins: bool = True,
|
1315
|
+
split_subq: bool = True,
|
1316
|
+
reuse_with_filtered_relation: bool = False,
|
1317
|
+
check_filterable: bool = True,
|
1318
|
+
summarize: bool = False,
|
1319
|
+
) -> tuple[WhereNode, set[str] | tuple[()]]:
|
1278
1320
|
"""
|
1279
1321
|
Build a WhereNode for a single filter clause but don't add it
|
1280
1322
|
to this Query. Query.add_q() will then add this filter to the where
|
@@ -1321,10 +1363,10 @@ class Query(BaseExpression):
|
|
1321
1363
|
raise TypeError("Cannot filter against a non-conditional expression.")
|
1322
1364
|
condition = filter_expr.resolve_expression(
|
1323
1365
|
self, allow_joins=allow_joins, summarize=summarize
|
1324
|
-
)
|
1366
|
+
) # type: ignore[attr-defined]
|
1325
1367
|
if not isinstance(condition, Lookup):
|
1326
1368
|
condition = self.build_lookup(["exact"], condition, True)
|
1327
|
-
return WhereNode([condition], connector=AND), []
|
1369
|
+
return WhereNode([condition], connector=AND), [] # type: ignore[return-value]
|
1328
1370
|
arg, value = filter_expr
|
1329
1371
|
if not arg:
|
1330
1372
|
raise FieldError(f"Cannot parse keyword query {arg!r}")
|
@@ -1347,7 +1389,7 @@ class Query(BaseExpression):
|
|
1347
1389
|
|
1348
1390
|
if reffed_expression:
|
1349
1391
|
condition = self.build_lookup(lookups, reffed_expression, value)
|
1350
|
-
return WhereNode([condition], connector=AND), []
|
1392
|
+
return WhereNode([condition], connector=AND), [] # type: ignore[return-value]
|
1351
1393
|
|
1352
1394
|
opts = self.get_meta()
|
1353
1395
|
alias = self.get_initial_alias()
|
@@ -1430,10 +1472,10 @@ class Query(BaseExpression):
|
|
1430
1472
|
clause.add(lookup_class(value, False), AND)
|
1431
1473
|
return clause, used_joins if not require_outer else ()
|
1432
1474
|
|
1433
|
-
def add_filter(self, filter_lhs, filter_rhs):
|
1475
|
+
def add_filter(self, filter_lhs: str, filter_rhs: Any) -> None:
|
1434
1476
|
self.add_q(Q((filter_lhs, filter_rhs)))
|
1435
1477
|
|
1436
|
-
def add_q(self, q_object):
|
1478
|
+
def add_q(self, q_object: Q) -> None: # type: ignore[unsupported-operator]
|
1437
1479
|
"""
|
1438
1480
|
A preprocessor for the internal _add_q(). Responsible for doing final
|
1439
1481
|
join promotion.
|
@@ -1452,23 +1494,23 @@ class Query(BaseExpression):
|
|
1452
1494
|
self.where.add(clause, AND)
|
1453
1495
|
self.demote_joins(existing_inner)
|
1454
1496
|
|
1455
|
-
def build_where(self, filter_expr):
|
1497
|
+
def build_where(self, filter_expr: tuple[str, Any]) -> WhereNode:
|
1456
1498
|
return self.build_filter(filter_expr, allow_joins=False)[0]
|
1457
1499
|
|
1458
|
-
def clear_where(self):
|
1500
|
+
def clear_where(self) -> None:
|
1459
1501
|
self.where = WhereNode()
|
1460
1502
|
|
1461
1503
|
def _add_q(
|
1462
1504
|
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
|
-
):
|
1505
|
+
q_object: Q,
|
1506
|
+
used_aliases: set[str] | None,
|
1507
|
+
branch_negated: bool = False,
|
1508
|
+
current_negated: bool = False,
|
1509
|
+
allow_joins: bool = True,
|
1510
|
+
split_subq: bool = True,
|
1511
|
+
check_filterable: bool = True,
|
1512
|
+
summarize: bool = False,
|
1513
|
+
) -> tuple[WhereNode, set[str] | tuple[()]]: # type: ignore[unsupported-operator]
|
1472
1514
|
"""Add a Q-object to the current filter."""
|
1473
1515
|
connector = q_object.connector
|
1474
1516
|
current_negated ^= q_object.negated
|
@@ -1495,8 +1537,12 @@ class Query(BaseExpression):
|
|
1495
1537
|
return target_clause, needed_inner
|
1496
1538
|
|
1497
1539
|
def build_filtered_relation_q(
|
1498
|
-
self,
|
1499
|
-
|
1540
|
+
self,
|
1541
|
+
q_object: Q,
|
1542
|
+
reuse: set[str],
|
1543
|
+
branch_negated: bool = False,
|
1544
|
+
current_negated: bool = False,
|
1545
|
+
) -> WhereNode: # type: ignore[unsupported-operator]
|
1500
1546
|
"""Add a FilteredRelation object to the current filter."""
|
1501
1547
|
connector = q_object.connector
|
1502
1548
|
current_negated ^= q_object.negated
|
@@ -1523,7 +1569,7 @@ class Query(BaseExpression):
|
|
1523
1569
|
target_clause.add(child_clause, connector)
|
1524
1570
|
return target_clause
|
1525
1571
|
|
1526
|
-
def add_filtered_relation(self, filtered_relation, alias):
|
1572
|
+
def add_filtered_relation(self, filtered_relation: Any, alias: str) -> None:
|
1527
1573
|
filtered_relation.alias = alias
|
1528
1574
|
lookups = dict(get_children_from_q(filtered_relation.condition))
|
1529
1575
|
relation_lookup_parts, relation_field_parts, _ = self.solve_lookup_type(
|
@@ -1553,7 +1599,13 @@ class Query(BaseExpression):
|
|
1553
1599
|
)
|
1554
1600
|
self._filtered_relations[filtered_relation.alias] = filtered_relation
|
1555
1601
|
|
1556
|
-
def names_to_path(
|
1602
|
+
def names_to_path(
|
1603
|
+
self,
|
1604
|
+
names: list[str],
|
1605
|
+
opts: Options | None,
|
1606
|
+
allow_many: bool = True,
|
1607
|
+
fail_on_missing: bool = False,
|
1608
|
+
) -> tuple[list[Any], Field, tuple[Field, ...], list[str]]:
|
1557
1609
|
"""
|
1558
1610
|
Walk the list of names and turns them into PathInfo tuples. A single
|
1559
1611
|
name in 'names' can generate multiple PathInfos (m2m, for example).
|
@@ -1593,7 +1645,7 @@ class Query(BaseExpression):
|
|
1593
1645
|
)
|
1594
1646
|
path.extend(filtered_relation_path[:-1])
|
1595
1647
|
else:
|
1596
|
-
field = opts.get_field(filtered_relation.relation_name)
|
1648
|
+
field = opts.get_field(filtered_relation.relation_name) # type: ignore[attr-defined]
|
1597
1649
|
if field is not None:
|
1598
1650
|
# Fields that contain one-to-many relations with a generic
|
1599
1651
|
# model (like a GenericForeignKey) cannot generate reverse
|
@@ -1655,13 +1707,13 @@ class Query(BaseExpression):
|
|
1655
1707
|
|
1656
1708
|
def setup_joins(
|
1657
1709
|
self,
|
1658
|
-
names,
|
1659
|
-
opts,
|
1660
|
-
alias,
|
1661
|
-
can_reuse=None,
|
1662
|
-
allow_many=True,
|
1663
|
-
reuse_with_filtered_relation=False,
|
1664
|
-
):
|
1710
|
+
names: list[str],
|
1711
|
+
opts: Options,
|
1712
|
+
alias: str,
|
1713
|
+
can_reuse: set[str] | None = None,
|
1714
|
+
allow_many: bool = True,
|
1715
|
+
reuse_with_filtered_relation: bool = False,
|
1716
|
+
) -> JoinInfo:
|
1665
1717
|
"""
|
1666
1718
|
Compute the necessary table joins for the passage through the fields
|
1667
1719
|
given in 'names'. 'opts' is the Options class for the current model
|
@@ -1698,10 +1750,10 @@ class Query(BaseExpression):
|
|
1698
1750
|
# directly, compute transforms here and create a partial that converts
|
1699
1751
|
# fields to the appropriate wrapped version.
|
1700
1752
|
|
1701
|
-
def final_transformer(field, alias):
|
1753
|
+
def final_transformer(field: Field, alias: str | None) -> Col:
|
1702
1754
|
if not self.alias_cols:
|
1703
1755
|
alias = None
|
1704
|
-
return field.get_col(alias)
|
1756
|
+
return field.get_col(alias) # type: ignore[arg-type]
|
1705
1757
|
|
1706
1758
|
# Try resolving all the names as fields first. If there's an error,
|
1707
1759
|
# treat trailing names as lookups until a field can be resolved.
|
@@ -1728,7 +1780,9 @@ class Query(BaseExpression):
|
|
1728
1780
|
break
|
1729
1781
|
for name in transforms:
|
1730
1782
|
|
1731
|
-
def transform(
|
1783
|
+
def transform(
|
1784
|
+
field: Field, alias: str | None, *, name: str, previous: Any
|
1785
|
+
) -> BaseExpression:
|
1732
1786
|
try:
|
1733
1787
|
wrapped = previous(field, alias)
|
1734
1788
|
return self.try_transform(wrapped, name)
|
@@ -1739,10 +1793,10 @@ class Query(BaseExpression):
|
|
1739
1793
|
else:
|
1740
1794
|
raise
|
1741
1795
|
|
1742
|
-
final_transformer = functools.partial(
|
1796
|
+
final_transformer = functools.partial( # type: ignore[misc]
|
1743
1797
|
transform, name=name, previous=final_transformer
|
1744
1798
|
)
|
1745
|
-
final_transformer.has_transforms = True
|
1799
|
+
final_transformer.has_transforms = True # type: ignore[attr-defined]
|
1746
1800
|
# Then, add the path to the query's joins. Note that we can't trim
|
1747
1801
|
# joins at this stage - we will need the information about join type
|
1748
1802
|
# of the trimmed joins.
|
@@ -1778,7 +1832,9 @@ class Query(BaseExpression):
|
|
1778
1832
|
filtered_relation.path = joins[:]
|
1779
1833
|
return JoinInfo(final_field, targets, opts, joins, path, final_transformer)
|
1780
1834
|
|
1781
|
-
def trim_joins(
|
1835
|
+
def trim_joins(
|
1836
|
+
self, targets: tuple[Field, ...], joins: list[str], path: list[Any]
|
1837
|
+
) -> tuple[tuple[Field, ...], str, list[str]]:
|
1782
1838
|
"""
|
1783
1839
|
The 'target' parameter is the final field being joined to, 'joins'
|
1784
1840
|
is the full list of join aliases. The 'path' contain the PathInfos
|
@@ -1811,7 +1867,12 @@ class Query(BaseExpression):
|
|
1811
1867
|
return targets, joins[-1], joins
|
1812
1868
|
|
1813
1869
|
@classmethod
|
1814
|
-
def _gen_cols(
|
1870
|
+
def _gen_cols(
|
1871
|
+
cls,
|
1872
|
+
exprs: TypingIterator[Any],
|
1873
|
+
include_external: bool = False,
|
1874
|
+
resolve_refs: bool = True,
|
1875
|
+
) -> TypingIterator[Col]:
|
1815
1876
|
for expr in exprs:
|
1816
1877
|
if isinstance(expr, Col):
|
1817
1878
|
yield expr
|
@@ -1829,10 +1890,16 @@ class Query(BaseExpression):
|
|
1829
1890
|
)
|
1830
1891
|
|
1831
1892
|
@classmethod
|
1832
|
-
def _gen_col_aliases(cls, exprs):
|
1893
|
+
def _gen_col_aliases(cls, exprs: TypingIterator[Any]) -> TypingIterator[str]:
|
1833
1894
|
yield from (expr.alias for expr in cls._gen_cols(exprs))
|
1834
1895
|
|
1835
|
-
def resolve_ref(
|
1896
|
+
def resolve_ref(
|
1897
|
+
self,
|
1898
|
+
name: str,
|
1899
|
+
allow_joins: bool = True,
|
1900
|
+
reuse: set[str] | None = None,
|
1901
|
+
summarize: bool = False,
|
1902
|
+
) -> BaseExpression:
|
1836
1903
|
annotation = self.annotations.get(name)
|
1837
1904
|
if annotation is not None:
|
1838
1905
|
if not allow_joins:
|
@@ -1882,7 +1949,12 @@ class Query(BaseExpression):
|
|
1882
1949
|
reuse.update(join_list)
|
1883
1950
|
return transform
|
1884
1951
|
|
1885
|
-
def split_exclude(
|
1952
|
+
def split_exclude(
|
1953
|
+
self,
|
1954
|
+
filter_expr: tuple[str, Any],
|
1955
|
+
can_reuse: set[str],
|
1956
|
+
names_with_path: list[tuple[str, list[Any]]],
|
1957
|
+
) -> tuple[WhereNode, set[str] | tuple[()]]:
|
1886
1958
|
"""
|
1887
1959
|
When doing an exclude against any kind of N-to-many relation, we need
|
1888
1960
|
to use a subquery. This method constructs the nested query, given the
|
@@ -1953,15 +2025,15 @@ class Query(BaseExpression):
|
|
1953
2025
|
# outercol IS NULL we will not match the row.
|
1954
2026
|
return condition, needed_inner
|
1955
2027
|
|
1956
|
-
def set_empty(self):
|
2028
|
+
def set_empty(self) -> None:
|
1957
2029
|
self.where.add(NothingNode(), AND)
|
1958
2030
|
for query in self.combined_queries:
|
1959
2031
|
query.set_empty()
|
1960
2032
|
|
1961
|
-
def is_empty(self):
|
2033
|
+
def is_empty(self) -> bool:
|
1962
2034
|
return any(isinstance(c, NothingNode) for c in self.where.children)
|
1963
2035
|
|
1964
|
-
def set_limits(self, low=None, high=None):
|
2036
|
+
def set_limits(self, low: int | None = None, high: int | None = None) -> None:
|
1965
2037
|
"""
|
1966
2038
|
Adjust the limits on the rows retrieved. Use low/high to set these,
|
1967
2039
|
as it makes it more Pythonic to read and write. When the SQL query is
|
@@ -1984,18 +2056,18 @@ class Query(BaseExpression):
|
|
1984
2056
|
if self.low_mark == self.high_mark:
|
1985
2057
|
self.set_empty()
|
1986
2058
|
|
1987
|
-
def clear_limits(self):
|
2059
|
+
def clear_limits(self) -> None:
|
1988
2060
|
"""Clear any existing limits."""
|
1989
2061
|
self.low_mark, self.high_mark = 0, None
|
1990
2062
|
|
1991
2063
|
@property
|
1992
|
-
def is_sliced(self):
|
2064
|
+
def is_sliced(self) -> bool:
|
1993
2065
|
return self.low_mark != 0 or self.high_mark is not None
|
1994
2066
|
|
1995
|
-
def has_limit_one(self):
|
2067
|
+
def has_limit_one(self) -> bool:
|
1996
2068
|
return self.high_mark is not None and (self.high_mark - self.low_mark) == 1
|
1997
2069
|
|
1998
|
-
def can_filter(self):
|
2070
|
+
def can_filter(self) -> bool:
|
1999
2071
|
"""
|
2000
2072
|
Return True if adding filters to this instance is still possible.
|
2001
2073
|
|
@@ -2003,7 +2075,7 @@ class Query(BaseExpression):
|
|
2003
2075
|
"""
|
2004
2076
|
return not self.is_sliced
|
2005
2077
|
|
2006
|
-
def clear_select_clause(self):
|
2078
|
+
def clear_select_clause(self) -> None:
|
2007
2079
|
"""Remove all fields from SELECT clause."""
|
2008
2080
|
self.select = ()
|
2009
2081
|
self.default_cols = False
|
@@ -2011,7 +2083,7 @@ class Query(BaseExpression):
|
|
2011
2083
|
self.set_extra_mask(())
|
2012
2084
|
self.set_annotation_mask(())
|
2013
2085
|
|
2014
|
-
def clear_select_fields(self):
|
2086
|
+
def clear_select_fields(self) -> None:
|
2015
2087
|
"""
|
2016
2088
|
Clear the list of fields to select (but not extra_select columns).
|
2017
2089
|
Some queryset types completely replace any existing list of select
|
@@ -2020,22 +2092,24 @@ class Query(BaseExpression):
|
|
2020
2092
|
self.select = ()
|
2021
2093
|
self.values_select = ()
|
2022
2094
|
|
2023
|
-
def add_select_col(self, col, name):
|
2095
|
+
def add_select_col(self, col: Col, name: str) -> None:
|
2024
2096
|
self.select += (col,)
|
2025
2097
|
self.values_select += (name,)
|
2026
2098
|
|
2027
|
-
def set_select(self, cols):
|
2099
|
+
def set_select(self, cols: list[Col] | tuple[Col, ...]) -> None:
|
2028
2100
|
self.default_cols = False
|
2029
2101
|
self.select = tuple(cols)
|
2030
2102
|
|
2031
|
-
def add_distinct_fields(self, *field_names):
|
2103
|
+
def add_distinct_fields(self, *field_names: str) -> None:
|
2032
2104
|
"""
|
2033
2105
|
Add and resolve the given fields to the query's "distinct on" clause.
|
2034
2106
|
"""
|
2035
2107
|
self.distinct_fields = field_names
|
2036
2108
|
self.distinct = True
|
2037
2109
|
|
2038
|
-
def add_fields(
|
2110
|
+
def add_fields(
|
2111
|
+
self, field_names: list[str] | TypingIterator[str], allow_m2m: bool = True
|
2112
|
+
) -> None:
|
2039
2113
|
"""
|
2040
2114
|
Add the given (model) fields to the select set. Add the field names in
|
2041
2115
|
the order specified.
|
@@ -2086,7 +2160,7 @@ class Query(BaseExpression):
|
|
2086
2160
|
)
|
2087
2161
|
)
|
2088
2162
|
|
2089
|
-
def add_ordering(self, *ordering):
|
2163
|
+
def add_ordering(self, *ordering: str | BaseExpression) -> None:
|
2090
2164
|
"""
|
2091
2165
|
Add items from the 'ordering' sequence to the query's "order by"
|
2092
2166
|
clause. These items are either field names (not column names) --
|
@@ -2122,7 +2196,7 @@ class Query(BaseExpression):
|
|
2122
2196
|
else:
|
2123
2197
|
self.default_ordering = False
|
2124
2198
|
|
2125
|
-
def clear_ordering(self, force=False, clear_default=True):
|
2199
|
+
def clear_ordering(self, force: bool = False, clear_default: bool = True) -> None:
|
2126
2200
|
"""
|
2127
2201
|
Remove any ordering settings if the current query allows it without
|
2128
2202
|
side effects, set 'force' to True to clear the ordering regardless.
|
@@ -2138,7 +2212,7 @@ class Query(BaseExpression):
|
|
2138
2212
|
if clear_default:
|
2139
2213
|
self.default_ordering = False
|
2140
2214
|
|
2141
|
-
def set_group_by(self, allow_aliases=True):
|
2215
|
+
def set_group_by(self, allow_aliases: bool = True) -> None:
|
2142
2216
|
"""
|
2143
2217
|
Expand the GROUP BY clause required by the query.
|
2144
2218
|
|
@@ -2171,7 +2245,7 @@ class Query(BaseExpression):
|
|
2171
2245
|
group_by.extend(group_by_cols)
|
2172
2246
|
self.group_by = tuple(group_by)
|
2173
2247
|
|
2174
|
-
def add_select_related(self, fields):
|
2248
|
+
def add_select_related(self, fields: list[str]) -> None:
|
2175
2249
|
"""
|
2176
2250
|
Set up the select_related data structure so that we only select
|
2177
2251
|
certain related models (as opposed to all models, when
|
@@ -2187,7 +2261,15 @@ class Query(BaseExpression):
|
|
2187
2261
|
d = d.setdefault(part, {})
|
2188
2262
|
self.select_related = field_dict
|
2189
2263
|
|
2190
|
-
def add_extra(
|
2264
|
+
def add_extra(
|
2265
|
+
self,
|
2266
|
+
select: dict[str, str],
|
2267
|
+
select_params: list[Any] | None,
|
2268
|
+
where: list[str],
|
2269
|
+
params: list[Any],
|
2270
|
+
tables: list[str],
|
2271
|
+
order_by: tuple[str, ...],
|
2272
|
+
) -> None:
|
2191
2273
|
"""
|
2192
2274
|
Add data to the various extra_* attributes for user-created additions
|
2193
2275
|
to the query.
|
@@ -2220,11 +2302,11 @@ class Query(BaseExpression):
|
|
2220
2302
|
if order_by:
|
2221
2303
|
self.extra_order_by = order_by
|
2222
2304
|
|
2223
|
-
def clear_deferred_loading(self):
|
2305
|
+
def clear_deferred_loading(self) -> None:
|
2224
2306
|
"""Remove any fields from the deferred loading set."""
|
2225
2307
|
self.deferred_loading = (frozenset(), True)
|
2226
2308
|
|
2227
|
-
def add_deferred_loading(self, field_names):
|
2309
|
+
def add_deferred_loading(self, field_names: frozenset[str]) -> None:
|
2228
2310
|
"""
|
2229
2311
|
Add the given list of model field names to the set of fields to
|
2230
2312
|
exclude from loading from the database when automatic column selection
|
@@ -2249,7 +2331,7 @@ class Query(BaseExpression):
|
|
2249
2331
|
if new_only := set(field_names).difference(existing):
|
2250
2332
|
self.deferred_loading = new_only, True
|
2251
2333
|
|
2252
|
-
def add_immediate_loading(self, field_names):
|
2334
|
+
def add_immediate_loading(self, field_names: list[str] | set[str]) -> None:
|
2253
2335
|
"""
|
2254
2336
|
Add the given list of model field names to the set of fields to
|
2255
2337
|
retrieve when the SQL is executed ("immediate loading" fields). The
|
@@ -2270,7 +2352,9 @@ class Query(BaseExpression):
|
|
2270
2352
|
# Replace any existing "immediate load" field names.
|
2271
2353
|
self.deferred_loading = frozenset(field_names), False
|
2272
2354
|
|
2273
|
-
def set_annotation_mask(
|
2355
|
+
def set_annotation_mask(
|
2356
|
+
self, names: set[str] | frozenset[str] | list[str] | dict[str, Any] | None
|
2357
|
+
) -> None: # type: ignore[misc]
|
2274
2358
|
"""Set the mask of annotations that will be returned by the SELECT."""
|
2275
2359
|
if names is None:
|
2276
2360
|
self.annotation_select_mask = None
|
@@ -2278,11 +2362,11 @@ class Query(BaseExpression):
|
|
2278
2362
|
self.annotation_select_mask = set(names)
|
2279
2363
|
self._annotation_select_cache = None
|
2280
2364
|
|
2281
|
-
def append_annotation_mask(self, names):
|
2365
|
+
def append_annotation_mask(self, names: list[str] | dict[str, Any]) -> None:
|
2282
2366
|
if self.annotation_select_mask is not None:
|
2283
2367
|
self.set_annotation_mask(self.annotation_select_mask.union(names))
|
2284
2368
|
|
2285
|
-
def set_extra_mask(self, names):
|
2369
|
+
def set_extra_mask(self, names: set[str] | tuple[str, ...] | None) -> None:
|
2286
2370
|
"""
|
2287
2371
|
Set the mask of extra select items that will be returned by SELECT.
|
2288
2372
|
Don't remove them from the Query since they might be used later.
|
@@ -2293,7 +2377,7 @@ class Query(BaseExpression):
|
|
2293
2377
|
self.extra_select_mask = set(names)
|
2294
2378
|
self._extra_select_cache = None
|
2295
2379
|
|
2296
|
-
def set_values(self, fields):
|
2380
|
+
def set_values(self, fields: list[str]) -> None:
|
2297
2381
|
self.select_related = False
|
2298
2382
|
self.clear_deferred_loading()
|
2299
2383
|
self.clear_select_fields()
|
@@ -2346,7 +2430,7 @@ class Query(BaseExpression):
|
|
2346
2430
|
self.add_fields(field_names, True)
|
2347
2431
|
|
2348
2432
|
@property
|
2349
|
-
def annotation_select(self):
|
2433
|
+
def annotation_select(self) -> dict[str, BaseExpression]:
|
2350
2434
|
"""
|
2351
2435
|
Return the dictionary of aggregate columns that are not masked and
|
2352
2436
|
should be used in the SELECT clause. Cache this result for performance.
|
@@ -2366,7 +2450,7 @@ class Query(BaseExpression):
|
|
2366
2450
|
return self.annotations
|
2367
2451
|
|
2368
2452
|
@property
|
2369
|
-
def extra_select(self):
|
2453
|
+
def extra_select(self) -> dict[str, tuple[str, list[Any]]]:
|
2370
2454
|
if self._extra_select_cache is not None:
|
2371
2455
|
return self._extra_select_cache
|
2372
2456
|
if not self.extra:
|
@@ -2379,7 +2463,9 @@ class Query(BaseExpression):
|
|
2379
2463
|
else:
|
2380
2464
|
return self.extra
|
2381
2465
|
|
2382
|
-
def trim_start(
|
2466
|
+
def trim_start(
|
2467
|
+
self, names_with_path: list[tuple[str, list[Any]]]
|
2468
|
+
) -> tuple[str, bool]:
|
2383
2469
|
"""
|
2384
2470
|
Trim joins from the start of the join path. The candidates for trim
|
2385
2471
|
are the PathInfos in names_with_path structure that are m2m joins.
|
@@ -2456,7 +2542,7 @@ class Query(BaseExpression):
|
|
2456
2542
|
self.set_select([f.get_col(select_alias) for f in select_fields])
|
2457
2543
|
return trimmed_prefix, contains_louter
|
2458
2544
|
|
2459
|
-
def is_nullable(self, field):
|
2545
|
+
def is_nullable(self, field: Field) -> bool:
|
2460
2546
|
"""Check if the given field should be treated as nullable."""
|
2461
2547
|
# QuerySet does not have knowledge of which connection is going to be
|
2462
2548
|
# used. For the single-database setup we always reference the default
|
@@ -2464,7 +2550,7 @@ class Query(BaseExpression):
|
|
2464
2550
|
return field.allow_null
|
2465
2551
|
|
2466
2552
|
|
2467
|
-
def get_order_dir(field, default="ASC"):
|
2553
|
+
def get_order_dir(field: str, default: str = "ASC") -> tuple[str, str]:
|
2468
2554
|
"""
|
2469
2555
|
Return the field name and direction for an order specification. For
|
2470
2556
|
example, '-foo' is returned as ('foo', 'DESC').
|
@@ -2484,7 +2570,7 @@ class JoinPromoter:
|
|
2484
2570
|
conditions.
|
2485
2571
|
"""
|
2486
2572
|
|
2487
|
-
def __init__(self, connector, num_children, negated):
|
2573
|
+
def __init__(self, connector: str, num_children: int, negated: bool):
|
2488
2574
|
self.connector = connector
|
2489
2575
|
self.negated = negated
|
2490
2576
|
if self.negated:
|
@@ -2499,20 +2585,20 @@ class JoinPromoter:
|
|
2499
2585
|
# inner and/or outer joins.
|
2500
2586
|
self.votes = Counter()
|
2501
2587
|
|
2502
|
-
def __repr__(self):
|
2588
|
+
def __repr__(self) -> str:
|
2503
2589
|
return (
|
2504
2590
|
f"{self.__class__.__qualname__}(connector={self.connector!r}, "
|
2505
2591
|
f"num_children={self.num_children!r}, negated={self.negated!r})"
|
2506
2592
|
)
|
2507
2593
|
|
2508
|
-
def add_votes(self, votes):
|
2594
|
+
def add_votes(self, votes: Any) -> None:
|
2509
2595
|
"""
|
2510
2596
|
Add single vote per item to self.votes. Parameter can be any
|
2511
2597
|
iterable.
|
2512
2598
|
"""
|
2513
2599
|
self.votes.update(votes)
|
2514
2600
|
|
2515
|
-
def update_join_types(self, query):
|
2601
|
+
def update_join_types(self, query: Query) -> set[str]:
|
2516
2602
|
"""
|
2517
2603
|
Change join types so that the generated query is as efficient as
|
2518
2604
|
possible, but still correct. So, change as many joins as possible
|