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