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/functions/text.py
CHANGED
@@ -1,12 +1,25 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING, Any
|
4
|
+
|
1
5
|
from plain.models.expressions import Func, Value
|
2
6
|
from plain.models.fields import CharField, IntegerField, TextField
|
3
7
|
from plain.models.functions import Cast, Coalesce
|
4
8
|
from plain.models.lookups import Transform
|
5
9
|
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from plain.models.backends.base.base import BaseDatabaseWrapper
|
12
|
+
from plain.models.sql.compiler import SQLCompiler
|
13
|
+
|
6
14
|
|
7
15
|
class MySQLSHA2Mixin:
|
8
|
-
def as_mysql(
|
9
|
-
|
16
|
+
def as_mysql(
|
17
|
+
self,
|
18
|
+
compiler: SQLCompiler,
|
19
|
+
connection: BaseDatabaseWrapper,
|
20
|
+
**extra_context: Any,
|
21
|
+
) -> tuple[str, tuple[Any, ...]]:
|
22
|
+
return super().as_sql( # type: ignore[misc]
|
10
23
|
compiler,
|
11
24
|
connection,
|
12
25
|
template=f"SHA2(%(expressions)s, {self.function[3:]})",
|
@@ -15,8 +28,13 @@ class MySQLSHA2Mixin:
|
|
15
28
|
|
16
29
|
|
17
30
|
class PostgreSQLSHAMixin:
|
18
|
-
def as_postgresql(
|
19
|
-
|
31
|
+
def as_postgresql(
|
32
|
+
self,
|
33
|
+
compiler: SQLCompiler,
|
34
|
+
connection: BaseDatabaseWrapper,
|
35
|
+
**extra_context: Any,
|
36
|
+
) -> tuple[str, tuple[Any, ...]]:
|
37
|
+
return super().as_sql( # type: ignore[misc]
|
20
38
|
compiler,
|
21
39
|
connection,
|
22
40
|
template="ENCODE(DIGEST(%(expressions)s, '%(function)s'), 'hex')",
|
@@ -29,7 +47,12 @@ class Chr(Transform):
|
|
29
47
|
function = "CHR"
|
30
48
|
lookup_name = "chr"
|
31
49
|
|
32
|
-
def as_mysql(
|
50
|
+
def as_mysql(
|
51
|
+
self,
|
52
|
+
compiler: SQLCompiler,
|
53
|
+
connection: BaseDatabaseWrapper,
|
54
|
+
**extra_context: Any,
|
55
|
+
) -> tuple[str, tuple[Any, ...]]:
|
33
56
|
return super().as_sql(
|
34
57
|
compiler,
|
35
58
|
connection,
|
@@ -38,7 +61,12 @@ class Chr(Transform):
|
|
38
61
|
**extra_context,
|
39
62
|
)
|
40
63
|
|
41
|
-
def as_sqlite(
|
64
|
+
def as_sqlite(
|
65
|
+
self,
|
66
|
+
compiler: SQLCompiler,
|
67
|
+
connection: BaseDatabaseWrapper,
|
68
|
+
**extra_context: Any,
|
69
|
+
) -> tuple[str, tuple[Any, ...]]:
|
42
70
|
return super().as_sql(compiler, connection, function="CHAR", **extra_context)
|
43
71
|
|
44
72
|
|
@@ -50,7 +78,12 @@ class ConcatPair(Func):
|
|
50
78
|
|
51
79
|
function = "CONCAT"
|
52
80
|
|
53
|
-
def as_sqlite(
|
81
|
+
def as_sqlite(
|
82
|
+
self,
|
83
|
+
compiler: SQLCompiler,
|
84
|
+
connection: BaseDatabaseWrapper,
|
85
|
+
**extra_context: Any,
|
86
|
+
) -> tuple[str, tuple[Any, ...]]:
|
54
87
|
coalesced = self.coalesce()
|
55
88
|
return super(ConcatPair, coalesced).as_sql(
|
56
89
|
compiler,
|
@@ -60,7 +93,12 @@ class ConcatPair(Func):
|
|
60
93
|
**extra_context,
|
61
94
|
)
|
62
95
|
|
63
|
-
def as_postgresql(
|
96
|
+
def as_postgresql(
|
97
|
+
self,
|
98
|
+
compiler: SQLCompiler,
|
99
|
+
connection: BaseDatabaseWrapper,
|
100
|
+
**extra_context: Any,
|
101
|
+
) -> tuple[str, tuple[Any, ...]]:
|
64
102
|
copy = self.copy()
|
65
103
|
copy.set_source_expressions(
|
66
104
|
[
|
@@ -74,7 +112,12 @@ class ConcatPair(Func):
|
|
74
112
|
**extra_context,
|
75
113
|
)
|
76
114
|
|
77
|
-
def as_mysql(
|
115
|
+
def as_mysql(
|
116
|
+
self,
|
117
|
+
compiler: SQLCompiler,
|
118
|
+
connection: BaseDatabaseWrapper,
|
119
|
+
**extra_context: Any,
|
120
|
+
) -> tuple[str, tuple[Any, ...]]:
|
78
121
|
# Use CONCAT_WS with an empty separator so that NULLs are ignored.
|
79
122
|
return super().as_sql(
|
80
123
|
compiler,
|
@@ -84,7 +127,7 @@ class ConcatPair(Func):
|
|
84
127
|
**extra_context,
|
85
128
|
)
|
86
129
|
|
87
|
-
def coalesce(self):
|
130
|
+
def coalesce(self) -> ConcatPair:
|
88
131
|
# null on either side results in null for expression, wrap with coalesce
|
89
132
|
c = self.copy()
|
90
133
|
c.set_source_expressions(
|
@@ -106,13 +149,13 @@ class Concat(Func):
|
|
106
149
|
function = None
|
107
150
|
template = "%(expressions)s"
|
108
151
|
|
109
|
-
def __init__(self, *expressions, **extra):
|
152
|
+
def __init__(self, *expressions: Any, **extra: Any) -> None:
|
110
153
|
if len(expressions) < 2:
|
111
154
|
raise ValueError("Concat must take at least two expressions")
|
112
155
|
paired = self._paired(expressions)
|
113
156
|
super().__init__(paired, **extra)
|
114
157
|
|
115
|
-
def _paired(self, expressions):
|
158
|
+
def _paired(self, expressions: tuple[Any, ...]) -> ConcatPair:
|
116
159
|
# wrap pairs of expressions in successive concat functions
|
117
160
|
# exp = [a, b, c, d]
|
118
161
|
# -> ConcatPair(a, ConcatPair(b, ConcatPair(c, d))))
|
@@ -126,7 +169,7 @@ class Left(Func):
|
|
126
169
|
arity = 2
|
127
170
|
output_field = CharField()
|
128
171
|
|
129
|
-
def __init__(self, expression, length, **extra):
|
172
|
+
def __init__(self, expression: Any, length: Any, **extra: Any) -> None:
|
130
173
|
"""
|
131
174
|
expression: the name of a field, or an expression returning a string
|
132
175
|
length: the number of characters to return from the start of the string
|
@@ -136,10 +179,15 @@ class Left(Func):
|
|
136
179
|
raise ValueError("'length' must be greater than 0.")
|
137
180
|
super().__init__(expression, length, **extra)
|
138
181
|
|
139
|
-
def get_substr(self):
|
182
|
+
def get_substr(self) -> Substr:
|
140
183
|
return Substr(self.source_expressions[0], Value(1), self.source_expressions[1])
|
141
184
|
|
142
|
-
def as_sqlite(
|
185
|
+
def as_sqlite(
|
186
|
+
self,
|
187
|
+
compiler: SQLCompiler,
|
188
|
+
connection: BaseDatabaseWrapper,
|
189
|
+
**extra_context: Any,
|
190
|
+
) -> tuple[str, tuple[Any, ...]]:
|
143
191
|
return self.get_substr().as_sqlite(compiler, connection, **extra_context)
|
144
192
|
|
145
193
|
|
@@ -150,7 +198,12 @@ class Length(Transform):
|
|
150
198
|
lookup_name = "length"
|
151
199
|
output_field = IntegerField()
|
152
200
|
|
153
|
-
def as_mysql(
|
201
|
+
def as_mysql(
|
202
|
+
self,
|
203
|
+
compiler: SQLCompiler,
|
204
|
+
connection: BaseDatabaseWrapper,
|
205
|
+
**extra_context: Any,
|
206
|
+
) -> tuple[str, tuple[Any, ...]]:
|
154
207
|
return super().as_sql(
|
155
208
|
compiler, connection, function="CHAR_LENGTH", **extra_context
|
156
209
|
)
|
@@ -165,7 +218,9 @@ class LPad(Func):
|
|
165
218
|
function = "LPAD"
|
166
219
|
output_field = CharField()
|
167
220
|
|
168
|
-
def __init__(
|
221
|
+
def __init__(
|
222
|
+
self, expression: Any, length: Any, fill_text: Any = Value(" "), **extra: Any
|
223
|
+
) -> None:
|
169
224
|
if (
|
170
225
|
not hasattr(length, "resolve_expression")
|
171
226
|
and length is not None
|
@@ -190,10 +245,20 @@ class Ord(Transform):
|
|
190
245
|
lookup_name = "ord"
|
191
246
|
output_field = IntegerField()
|
192
247
|
|
193
|
-
def as_mysql(
|
248
|
+
def as_mysql(
|
249
|
+
self,
|
250
|
+
compiler: SQLCompiler,
|
251
|
+
connection: BaseDatabaseWrapper,
|
252
|
+
**extra_context: Any,
|
253
|
+
) -> tuple[str, tuple[Any, ...]]:
|
194
254
|
return super().as_sql(compiler, connection, function="ORD", **extra_context)
|
195
255
|
|
196
|
-
def as_sqlite(
|
256
|
+
def as_sqlite(
|
257
|
+
self,
|
258
|
+
compiler: SQLCompiler,
|
259
|
+
connection: BaseDatabaseWrapper,
|
260
|
+
**extra_context: Any,
|
261
|
+
) -> tuple[str, tuple[Any, ...]]:
|
197
262
|
return super().as_sql(compiler, connection, function="UNICODE", **extra_context)
|
198
263
|
|
199
264
|
|
@@ -201,7 +266,7 @@ class Repeat(Func):
|
|
201
266
|
function = "REPEAT"
|
202
267
|
output_field = CharField()
|
203
268
|
|
204
|
-
def __init__(self, expression, number, **extra):
|
269
|
+
def __init__(self, expression: Any, number: Any, **extra: Any) -> None:
|
205
270
|
if (
|
206
271
|
not hasattr(number, "resolve_expression")
|
207
272
|
and number is not None
|
@@ -214,7 +279,9 @@ class Repeat(Func):
|
|
214
279
|
class Replace(Func):
|
215
280
|
function = "REPLACE"
|
216
281
|
|
217
|
-
def __init__(
|
282
|
+
def __init__(
|
283
|
+
self, expression: Any, text: Any, replacement: Any = Value(""), **extra: Any
|
284
|
+
) -> None:
|
218
285
|
super().__init__(expression, text, replacement, **extra)
|
219
286
|
|
220
287
|
|
@@ -226,7 +293,7 @@ class Reverse(Transform):
|
|
226
293
|
class Right(Left):
|
227
294
|
function = "RIGHT"
|
228
295
|
|
229
|
-
def get_substr(self):
|
296
|
+
def get_substr(self) -> Substr:
|
230
297
|
return Substr(
|
231
298
|
self.source_expressions[0], self.source_expressions[1] * Value(-1)
|
232
299
|
)
|
@@ -277,7 +344,12 @@ class StrIndex(Func):
|
|
277
344
|
arity = 2
|
278
345
|
output_field = IntegerField()
|
279
346
|
|
280
|
-
def as_postgresql(
|
347
|
+
def as_postgresql(
|
348
|
+
self,
|
349
|
+
compiler: SQLCompiler,
|
350
|
+
connection: BaseDatabaseWrapper,
|
351
|
+
**extra_context: Any,
|
352
|
+
) -> tuple[str, tuple[Any, ...]]:
|
281
353
|
return super().as_sql(compiler, connection, function="STRPOS", **extra_context)
|
282
354
|
|
283
355
|
|
@@ -285,7 +357,9 @@ class Substr(Func):
|
|
285
357
|
function = "SUBSTRING"
|
286
358
|
output_field = CharField()
|
287
359
|
|
288
|
-
def __init__(
|
360
|
+
def __init__(
|
361
|
+
self, expression: Any, pos: Any, length: Any = None, **extra: Any
|
362
|
+
) -> None:
|
289
363
|
"""
|
290
364
|
expression: the name of a field, or an expression returning a string
|
291
365
|
pos: an integer > 0, or an expression returning an integer
|
@@ -299,7 +373,12 @@ class Substr(Func):
|
|
299
373
|
expressions.append(length)
|
300
374
|
super().__init__(*expressions, **extra)
|
301
375
|
|
302
|
-
def as_sqlite(
|
376
|
+
def as_sqlite(
|
377
|
+
self,
|
378
|
+
compiler: SQLCompiler,
|
379
|
+
connection: BaseDatabaseWrapper,
|
380
|
+
**extra_context: Any,
|
381
|
+
) -> tuple[str, tuple[Any, ...]]:
|
303
382
|
return super().as_sql(compiler, connection, function="SUBSTR", **extra_context)
|
304
383
|
|
305
384
|
|
plain/models/functions/window.py
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Any
|
4
|
+
|
1
5
|
from plain.models.expressions import Func
|
2
|
-
from plain.models.fields import FloatField, IntegerField
|
6
|
+
from plain.models.fields import Field, FloatField, IntegerField
|
3
7
|
|
4
8
|
__all__ = [
|
5
9
|
"CumeDist",
|
@@ -37,7 +41,9 @@ class FirstValue(Func):
|
|
37
41
|
class LagLeadFunction(Func):
|
38
42
|
window_compatible = True
|
39
43
|
|
40
|
-
def __init__(
|
44
|
+
def __init__(
|
45
|
+
self, expression: Any, offset: int = 1, default: Any = None, **extra: Any
|
46
|
+
) -> None:
|
41
47
|
if expression is None:
|
42
48
|
raise ValueError(
|
43
49
|
f"{self.__class__.__name__} requires a non-null source expression."
|
@@ -51,7 +57,7 @@ class LagLeadFunction(Func):
|
|
51
57
|
args += (default,)
|
52
58
|
super().__init__(*args, **extra)
|
53
59
|
|
54
|
-
def _resolve_output_field(self):
|
60
|
+
def _resolve_output_field(self) -> Field:
|
55
61
|
sources = self.get_source_expressions()
|
56
62
|
return sources[0].output_field
|
57
63
|
|
@@ -74,7 +80,7 @@ class NthValue(Func):
|
|
74
80
|
function = "NTH_VALUE"
|
75
81
|
window_compatible = True
|
76
82
|
|
77
|
-
def __init__(self, expression, nth=1, **extra):
|
83
|
+
def __init__(self, expression: Any, nth: int = 1, **extra: Any) -> None:
|
78
84
|
if expression is None:
|
79
85
|
raise ValueError(
|
80
86
|
f"{self.__class__.__name__} requires a non-null source expression."
|
@@ -85,7 +91,7 @@ class NthValue(Func):
|
|
85
91
|
)
|
86
92
|
super().__init__(expression, nth, **extra)
|
87
93
|
|
88
|
-
def _resolve_output_field(self):
|
94
|
+
def _resolve_output_field(self) -> Field:
|
89
95
|
sources = self.get_source_expressions()
|
90
96
|
return sources[0].output_field
|
91
97
|
|
@@ -95,7 +101,7 @@ class Ntile(Func):
|
|
95
101
|
output_field = IntegerField()
|
96
102
|
window_compatible = True
|
97
103
|
|
98
|
-
def __init__(self, num_buckets=1, **extra):
|
104
|
+
def __init__(self, num_buckets: int = 1, **extra: Any) -> None:
|
99
105
|
if num_buckets <= 0:
|
100
106
|
raise ValueError("num_buckets must be greater than 0.")
|
101
107
|
super().__init__(num_buckets, **extra)
|
plain/models/indexes.py
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
from types import NoneType
|
4
|
+
from typing import TYPE_CHECKING, Any
|
2
5
|
|
3
6
|
from plain.models.backends.utils import names_digest, split_identifier
|
4
7
|
from plain.models.expressions import Col, ExpressionList, F, Func, OrderBy
|
@@ -7,6 +10,14 @@ from plain.models.query_utils import Q
|
|
7
10
|
from plain.models.sql import Query
|
8
11
|
from plain.utils.functional import partition
|
9
12
|
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from plain.models.backends.base.base import BaseDatabaseWrapper
|
15
|
+
from plain.models.backends.base.schema import BaseDatabaseSchemaEditor
|
16
|
+
from plain.models.backends.ddl_references import Statement
|
17
|
+
from plain.models.base import Model
|
18
|
+
from plain.models.expressions import Expression
|
19
|
+
from plain.models.sql.compiler import SQLCompiler
|
20
|
+
|
10
21
|
__all__ = ["Index"]
|
11
22
|
|
12
23
|
|
@@ -18,13 +29,13 @@ class Index:
|
|
18
29
|
|
19
30
|
def __init__(
|
20
31
|
self,
|
21
|
-
*expressions,
|
22
|
-
fields=(),
|
23
|
-
name=None,
|
24
|
-
opclasses=(),
|
25
|
-
condition=None,
|
26
|
-
include=None,
|
27
|
-
):
|
32
|
+
*expressions: Any,
|
33
|
+
fields: tuple[str, ...] | list[str] = (),
|
34
|
+
name: str | None = None,
|
35
|
+
opclasses: tuple[str, ...] | list[str] = (),
|
36
|
+
condition: Q | None = None,
|
37
|
+
include: tuple[str, ...] | list[str] | None = None,
|
38
|
+
) -> None:
|
28
39
|
if opclasses and not name:
|
29
40
|
raise ValueError("An index must be named to use opclasses.")
|
30
41
|
if not isinstance(condition, NoneType | Q):
|
@@ -77,10 +88,12 @@ class Index:
|
|
77
88
|
)
|
78
89
|
|
79
90
|
@property
|
80
|
-
def contains_expressions(self):
|
91
|
+
def contains_expressions(self) -> bool:
|
81
92
|
return bool(self.expressions)
|
82
93
|
|
83
|
-
def _get_condition_sql(
|
94
|
+
def _get_condition_sql(
|
95
|
+
self, model: type[Model], schema_editor: BaseDatabaseSchemaEditor
|
96
|
+
) -> str | None:
|
84
97
|
if self.condition is None:
|
85
98
|
return None
|
86
99
|
query = Query(model=model, alias_cols=False)
|
@@ -89,7 +102,9 @@ class Index:
|
|
89
102
|
sql, params = where.as_sql(compiler, schema_editor.connection)
|
90
103
|
return sql % tuple(schema_editor.quote_value(p) for p in params)
|
91
104
|
|
92
|
-
def create_sql(
|
105
|
+
def create_sql(
|
106
|
+
self, model: type[Model], schema_editor: BaseDatabaseSchemaEditor, **kwargs: Any
|
107
|
+
) -> Statement:
|
93
108
|
include = [
|
94
109
|
model._meta.get_field(field_name).column for field_name in self.include
|
95
110
|
]
|
@@ -111,15 +126,15 @@ class Index:
|
|
111
126
|
for field_name, _ in self.fields_orders
|
112
127
|
]
|
113
128
|
if schema_editor.connection.features.supports_index_column_ordering:
|
114
|
-
col_suffixes =
|
129
|
+
col_suffixes = tuple(order[1] for order in self.fields_orders)
|
115
130
|
else:
|
116
|
-
col_suffixes =
|
131
|
+
col_suffixes = ("",) * len(self.fields_orders)
|
117
132
|
expressions = None
|
118
133
|
return schema_editor._create_index_sql(
|
119
134
|
model,
|
120
135
|
fields=fields,
|
121
136
|
name=self.name,
|
122
|
-
col_suffixes=col_suffixes,
|
137
|
+
col_suffixes=col_suffixes, # type: ignore[arg-type]
|
123
138
|
opclasses=self.opclasses,
|
124
139
|
condition=condition,
|
125
140
|
include=include,
|
@@ -127,10 +142,12 @@ class Index:
|
|
127
142
|
**kwargs,
|
128
143
|
)
|
129
144
|
|
130
|
-
def remove_sql(
|
145
|
+
def remove_sql(
|
146
|
+
self, model: type[Model], schema_editor: BaseDatabaseSchemaEditor, **kwargs: Any
|
147
|
+
) -> Statement:
|
131
148
|
return schema_editor._delete_index_sql(model, self.name, **kwargs)
|
132
149
|
|
133
|
-
def deconstruct(self):
|
150
|
+
def deconstruct(self) -> tuple[str, tuple[Expression, ...], dict[str, Any]]:
|
134
151
|
path = f"{self.__class__.__module__}.{self.__class__.__name__}"
|
135
152
|
path = path.replace("plain.models.indexes", "plain.models")
|
136
153
|
kwargs = {"name": self.name}
|
@@ -144,12 +161,12 @@ class Index:
|
|
144
161
|
kwargs["include"] = self.include
|
145
162
|
return (path, self.expressions, kwargs)
|
146
163
|
|
147
|
-
def clone(self):
|
164
|
+
def clone(self) -> Index:
|
148
165
|
"""Create a copy of this Index."""
|
149
166
|
_, args, kwargs = self.deconstruct()
|
150
167
|
return self.__class__(*args, **kwargs)
|
151
168
|
|
152
|
-
def set_name_with_model(self, model):
|
169
|
+
def set_name_with_model(self, model: type[Model]) -> None:
|
153
170
|
"""
|
154
171
|
Generate a unique name for the index.
|
155
172
|
|
@@ -184,7 +201,7 @@ class Index:
|
|
184
201
|
if self.name[0] == "_" or self.name[0].isdigit():
|
185
202
|
self.name = f"D{self.name[1:]}"
|
186
203
|
|
187
|
-
def __repr__(self):
|
204
|
+
def __repr__(self) -> str:
|
188
205
|
return "<{}:{}{}{}{}{}{}>".format(
|
189
206
|
self.__class__.__qualname__,
|
190
207
|
"" if not self.fields else f" fields={repr(self.fields)}",
|
@@ -195,9 +212,9 @@ class Index:
|
|
195
212
|
"" if not self.opclasses else f" opclasses={repr(self.opclasses)}",
|
196
213
|
)
|
197
214
|
|
198
|
-
def __eq__(self, other):
|
215
|
+
def __eq__(self, other: object) -> bool:
|
199
216
|
if self.__class__ == other.__class__:
|
200
|
-
return self.deconstruct() == other.deconstruct()
|
217
|
+
return self.deconstruct() == other.deconstruct() # type: ignore[attr-defined]
|
201
218
|
return NotImplemented
|
202
219
|
|
203
220
|
|
@@ -207,7 +224,9 @@ class IndexExpression(Func):
|
|
207
224
|
template = "%(expressions)s"
|
208
225
|
wrapper_classes = (OrderBy, Collate)
|
209
226
|
|
210
|
-
def set_wrapper_classes(
|
227
|
+
def set_wrapper_classes(
|
228
|
+
self, connection: BaseDatabaseWrapper | None = None
|
229
|
+
) -> None:
|
211
230
|
# Some databases (e.g. MySQL) treats COLLATE as an indexed expression.
|
212
231
|
if connection and connection.features.collate_as_index_expression:
|
213
232
|
self.wrapper_classes = tuple(
|
@@ -220,12 +239,12 @@ class IndexExpression(Func):
|
|
220
239
|
|
221
240
|
def resolve_expression(
|
222
241
|
self,
|
223
|
-
query=None,
|
224
|
-
allow_joins=True,
|
225
|
-
reuse=None,
|
226
|
-
summarize=False,
|
227
|
-
for_save=False,
|
228
|
-
):
|
242
|
+
query: Any = None,
|
243
|
+
allow_joins: bool = True,
|
244
|
+
reuse: Any = None,
|
245
|
+
summarize: bool = False,
|
246
|
+
for_save: bool = False,
|
247
|
+
) -> Expression:
|
229
248
|
expressions = list(self.flatten())
|
230
249
|
# Split expressions and wrappers.
|
231
250
|
index_expressions, wrappers = partition(
|
@@ -287,6 +306,11 @@ class IndexExpression(Func):
|
|
287
306
|
query, allow_joins, reuse, summarize, for_save
|
288
307
|
)
|
289
308
|
|
290
|
-
def as_sqlite(
|
309
|
+
def as_sqlite(
|
310
|
+
self,
|
311
|
+
compiler: SQLCompiler,
|
312
|
+
connection: BaseDatabaseWrapper,
|
313
|
+
**extra_context: Any,
|
314
|
+
) -> tuple[str, tuple[Any, ...]]:
|
291
315
|
# Casting to numeric is unnecessary.
|
292
316
|
return self.as_sql(compiler, connection, **extra_context)
|