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
@@ -46,7 +46,10 @@ reverse many-to-one relation.
|
|
46
46
|
``ReverseManyToManyDescriptor``, use ``ManyToManyDescriptor`` instead.
|
47
47
|
"""
|
48
48
|
|
49
|
+
from __future__ import annotations
|
50
|
+
|
49
51
|
from functools import cached_property
|
52
|
+
from typing import Any
|
50
53
|
|
51
54
|
from plain.models.query import QuerySet
|
52
55
|
from plain.models.query_utils import DeferredAttribute
|
@@ -60,7 +63,7 @@ from .related_managers import (
|
|
60
63
|
|
61
64
|
|
62
65
|
class ForeignKeyDeferredAttribute(DeferredAttribute):
|
63
|
-
def __set__(self, instance, value):
|
66
|
+
def __set__(self, instance: Any, value: Any) -> None:
|
64
67
|
if instance.__dict__.get(self.field.attname) != value and self.field.is_cached(
|
65
68
|
instance
|
66
69
|
):
|
@@ -80,11 +83,11 @@ class ForwardManyToOneDescriptor:
|
|
80
83
|
``Child.parent`` is a ``ForwardManyToOneDescriptor`` instance.
|
81
84
|
"""
|
82
85
|
|
83
|
-
def __init__(self, field_with_rel):
|
86
|
+
def __init__(self, field_with_rel: Any) -> None:
|
84
87
|
self.field = field_with_rel
|
85
88
|
|
86
89
|
@cached_property
|
87
|
-
def RelatedObjectDoesNotExist(self):
|
90
|
+
def RelatedObjectDoesNotExist(self) -> type:
|
88
91
|
# The exception can't be created at initialization time since the
|
89
92
|
# related model might not be resolved yet; `self.field.model` might
|
90
93
|
# still be a string model reference.
|
@@ -97,14 +100,16 @@ class ForwardManyToOneDescriptor:
|
|
97
100
|
},
|
98
101
|
)
|
99
102
|
|
100
|
-
def is_cached(self, instance):
|
103
|
+
def is_cached(self, instance: Any) -> bool:
|
101
104
|
return self.field.is_cached(instance)
|
102
105
|
|
103
106
|
def get_queryset(self) -> QuerySet:
|
104
|
-
qs = self.field.remote_field.model.
|
107
|
+
qs = self.field.remote_field.model._model_meta.base_queryset
|
105
108
|
return qs.all()
|
106
109
|
|
107
|
-
def get_prefetch_queryset(
|
110
|
+
def get_prefetch_queryset(
|
111
|
+
self, instances: list[Any], queryset: QuerySet | None = None
|
112
|
+
) -> tuple[QuerySet, Any, Any, bool, str, bool]:
|
108
113
|
if queryset is None:
|
109
114
|
queryset = self.get_queryset()
|
110
115
|
|
@@ -145,12 +150,14 @@ class ForwardManyToOneDescriptor:
|
|
145
150
|
False,
|
146
151
|
)
|
147
152
|
|
148
|
-
def get_object(self, instance):
|
153
|
+
def get_object(self, instance: Any) -> Any:
|
149
154
|
qs = self.get_queryset()
|
150
155
|
# Assuming the database enforces foreign keys, this won't fail.
|
151
156
|
return qs.get(self.field.get_reverse_related_filter(instance))
|
152
157
|
|
153
|
-
def __get__(
|
158
|
+
def __get__(
|
159
|
+
self, instance: Any | None, cls: type | None = None
|
160
|
+
) -> ForwardManyToOneDescriptor | Any | None:
|
154
161
|
"""
|
155
162
|
Get the related instance through the forward relation.
|
156
163
|
|
@@ -189,7 +196,7 @@ class ForwardManyToOneDescriptor:
|
|
189
196
|
else:
|
190
197
|
return rel_obj
|
191
198
|
|
192
|
-
def __set__(self, instance, value):
|
199
|
+
def __set__(self, instance: Any, value: Any) -> None:
|
193
200
|
"""
|
194
201
|
Set the related instance through the forward relation.
|
195
202
|
|
@@ -210,7 +217,7 @@ class ForwardManyToOneDescriptor:
|
|
210
217
|
# An object must be an instance of the related class.
|
211
218
|
if value is not None and not isinstance(value, self.field.remote_field.model):
|
212
219
|
raise ValueError(
|
213
|
-
f'Cannot assign "{value!r}": "{instance.
|
220
|
+
f'Cannot assign "{value!r}": "{instance.model_options.object_name}.{self.field.name}" must be a "{self.field.remote_field.model.model_options.object_name}" instance.'
|
214
221
|
)
|
215
222
|
remote_field = self.field.remote_field
|
216
223
|
# If we're setting the value of a OneToOneField to None, we need to clear
|
@@ -250,7 +257,7 @@ class ForwardManyToOneDescriptor:
|
|
250
257
|
if value is not None and not remote_field.multiple:
|
251
258
|
remote_field.set_cached_value(value, instance)
|
252
259
|
|
253
|
-
def __reduce__(self):
|
260
|
+
def __reduce__(self) -> tuple[Any, tuple[Any, str]]:
|
254
261
|
"""
|
255
262
|
Pickling should return the instance attached by self.field on the
|
256
263
|
model, not a new copy of that descriptor. Use getattr() to retrieve
|
@@ -268,11 +275,13 @@ class RelationDescriptorBase:
|
|
268
275
|
this because they allow direct assignment.
|
269
276
|
"""
|
270
277
|
|
271
|
-
def __init__(self, rel):
|
278
|
+
def __init__(self, rel: Any) -> None:
|
272
279
|
self.rel = rel
|
273
280
|
self.field = rel.field
|
274
281
|
|
275
|
-
def __get__(
|
282
|
+
def __get__(
|
283
|
+
self, instance: Any | None, cls: type | None = None
|
284
|
+
) -> RelationDescriptorBase | Any:
|
276
285
|
"""
|
277
286
|
Get the related manager when the descriptor is accessed.
|
278
287
|
|
@@ -282,19 +291,19 @@ class RelationDescriptorBase:
|
|
282
291
|
return self
|
283
292
|
return self.get_related_manager(instance)
|
284
293
|
|
285
|
-
def get_related_manager(self, instance):
|
294
|
+
def get_related_manager(self, instance: Any) -> Any:
|
286
295
|
"""Return the appropriate manager for this relation."""
|
287
296
|
raise NotImplementedError(
|
288
297
|
f"{self.__class__.__name__} must implement get_related_manager()"
|
289
298
|
)
|
290
299
|
|
291
|
-
def _get_set_deprecation_msg_params(self):
|
300
|
+
def _get_set_deprecation_msg_params(self) -> tuple[str, str]:
|
292
301
|
"""Return parameters for the error message when direct assignment is attempted."""
|
293
302
|
raise NotImplementedError(
|
294
303
|
f"{self.__class__.__name__} must implement _get_set_deprecation_msg_params()"
|
295
304
|
)
|
296
305
|
|
297
|
-
def __set__(self, instance, value):
|
306
|
+
def __set__(self, instance: Any, value: Any) -> None:
|
298
307
|
"""Prevent direct assignment to the relation."""
|
299
308
|
raise TypeError(
|
300
309
|
"Direct assignment to the {} is prohibited. Use {}.set() instead.".format(
|
@@ -318,11 +327,11 @@ class ReverseManyToOneDescriptor(RelationDescriptorBase):
|
|
318
327
|
Most of the implementation is delegated to the ReverseManyToOneManager class.
|
319
328
|
"""
|
320
329
|
|
321
|
-
def get_related_manager(self, instance):
|
330
|
+
def get_related_manager(self, instance: Any) -> ReverseManyToOneManager:
|
322
331
|
"""Return the ReverseManyToOneManager for this relation."""
|
323
332
|
return ReverseManyToOneManager(instance, self.rel)
|
324
333
|
|
325
|
-
def _get_set_deprecation_msg_params(self):
|
334
|
+
def _get_set_deprecation_msg_params(self) -> tuple[str, str]:
|
326
335
|
return (
|
327
336
|
"reverse side of a related set",
|
328
337
|
self.rel.get_accessor_name(),
|
@@ -343,17 +352,17 @@ class ForwardManyToManyDescriptor(RelationDescriptorBase):
|
|
343
352
|
"""
|
344
353
|
|
345
354
|
@property
|
346
|
-
def through(self):
|
355
|
+
def through(self) -> Any:
|
347
356
|
# through is provided so that you have easy access to the through
|
348
357
|
# model (Book.authors.through) for inlines, etc. This is done as
|
349
358
|
# a property to ensure that the fully resolved value is returned.
|
350
359
|
return self.rel.through
|
351
360
|
|
352
|
-
def get_related_manager(self, instance):
|
361
|
+
def get_related_manager(self, instance: Any) -> ForwardManyToManyManager:
|
353
362
|
"""Return the ForwardManyToManyManager for this relation."""
|
354
363
|
return ForwardManyToManyManager(instance, self.rel)
|
355
364
|
|
356
|
-
def _get_set_deprecation_msg_params(self):
|
365
|
+
def _get_set_deprecation_msg_params(self) -> tuple[str, str]:
|
357
366
|
return (
|
358
367
|
"forward side of a many-to-many set",
|
359
368
|
self.field.name,
|
@@ -374,17 +383,17 @@ class ReverseManyToManyDescriptor(RelationDescriptorBase):
|
|
374
383
|
"""
|
375
384
|
|
376
385
|
@property
|
377
|
-
def through(self):
|
386
|
+
def through(self) -> Any:
|
378
387
|
# through is provided so that you have easy access to the through
|
379
388
|
# model (Book.authors.through) for inlines, etc. This is done as
|
380
389
|
# a property to ensure that the fully resolved value is returned.
|
381
390
|
return self.rel.through
|
382
391
|
|
383
|
-
def get_related_manager(self, instance):
|
392
|
+
def get_related_manager(self, instance: Any) -> ReverseManyToManyManager:
|
384
393
|
"""Return the ReverseManyToManyManager for this relation."""
|
385
394
|
return ReverseManyToManyManager(instance, self.rel)
|
386
395
|
|
387
|
-
def _get_set_deprecation_msg_params(self):
|
396
|
+
def _get_set_deprecation_msg_params(self) -> tuple[str, str]:
|
388
397
|
return (
|
389
398
|
"reverse side of a many-to-many set",
|
390
399
|
self.rel.get_accessor_name(),
|
@@ -1,3 +1,7 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING, Any
|
4
|
+
|
1
5
|
from plain.models.lookups import (
|
2
6
|
Exact,
|
3
7
|
GreaterThan,
|
@@ -8,12 +12,16 @@ from plain.models.lookups import (
|
|
8
12
|
LessThanOrEqual,
|
9
13
|
)
|
10
14
|
|
15
|
+
if TYPE_CHECKING:
|
16
|
+
from plain.models.backends.base.base import BaseDatabaseWrapper
|
17
|
+
from plain.models.sql.compiler import SQLCompiler
|
18
|
+
|
11
19
|
|
12
20
|
class MultiColSource:
|
13
21
|
contains_aggregate = False
|
14
22
|
contains_over_clause = False
|
15
23
|
|
16
|
-
def __init__(self, alias, targets, sources, field):
|
24
|
+
def __init__(self, alias: str, targets: Any, sources: Any, field: Any) -> None:
|
17
25
|
self.targets, self.sources, self.field, self.alias = (
|
18
26
|
targets,
|
19
27
|
sources,
|
@@ -22,22 +30,22 @@ class MultiColSource:
|
|
22
30
|
)
|
23
31
|
self.output_field = self.field
|
24
32
|
|
25
|
-
def __repr__(self):
|
33
|
+
def __repr__(self) -> str:
|
26
34
|
return f"{self.__class__.__name__}({self.alias}, {self.field})"
|
27
35
|
|
28
|
-
def relabeled_clone(self, relabels):
|
36
|
+
def relabeled_clone(self, relabels: dict[str, str]) -> MultiColSource:
|
29
37
|
return self.__class__(
|
30
38
|
relabels.get(self.alias, self.alias), self.targets, self.sources, self.field
|
31
39
|
)
|
32
40
|
|
33
|
-
def get_lookup(self, lookup):
|
41
|
+
def get_lookup(self, lookup: str) -> Any:
|
34
42
|
return self.output_field.get_lookup(lookup)
|
35
43
|
|
36
|
-
def resolve_expression(self, *args, **kwargs):
|
44
|
+
def resolve_expression(self, *args: Any, **kwargs: Any) -> MultiColSource:
|
37
45
|
return self
|
38
46
|
|
39
47
|
|
40
|
-
def get_normalized_value(value, lhs):
|
48
|
+
def get_normalized_value(value: Any, lhs: Any) -> tuple[Any, ...]:
|
41
49
|
from plain.models import Model
|
42
50
|
|
43
51
|
if isinstance(value, Model):
|
@@ -47,7 +55,7 @@ def get_normalized_value(value, lhs):
|
|
47
55
|
sources = lhs.output_field.path_infos[-1].target_fields
|
48
56
|
for source in sources:
|
49
57
|
while not isinstance(value, source.model) and source.remote_field:
|
50
|
-
source = source.remote_field.model.
|
58
|
+
source = source.remote_field.model._model_meta.get_field(
|
51
59
|
source.remote_field.field_name
|
52
60
|
)
|
53
61
|
try:
|
@@ -63,7 +71,7 @@ def get_normalized_value(value, lhs):
|
|
63
71
|
|
64
72
|
|
65
73
|
class RelatedIn(In):
|
66
|
-
def get_prep_lookup(self):
|
74
|
+
def get_prep_lookup(self) -> list[Any]: # type: ignore[misc]
|
67
75
|
if not isinstance(self.lhs, MultiColSource):
|
68
76
|
if self.rhs_is_direct_value():
|
69
77
|
# If we get here, we are dealing with single-column relations.
|
@@ -97,7 +105,9 @@ class RelatedIn(In):
|
|
97
105
|
self.rhs.set_values([target_field])
|
98
106
|
return super().get_prep_lookup()
|
99
107
|
|
100
|
-
def as_sql(
|
108
|
+
def as_sql(
|
109
|
+
self, compiler: SQLCompiler, connection: BaseDatabaseWrapper
|
110
|
+
) -> tuple[str, list[Any]]: # type: ignore[misc]
|
101
111
|
if isinstance(self.lhs, MultiColSource):
|
102
112
|
# For multicolumn lookups we need to build a multicolumn where clause.
|
103
113
|
# This clause is either a SubqueryConstraint (for values that need
|
@@ -139,7 +149,7 @@ class RelatedIn(In):
|
|
139
149
|
|
140
150
|
|
141
151
|
class RelatedLookupMixin:
|
142
|
-
def get_prep_lookup(self):
|
152
|
+
def get_prep_lookup(self) -> Any: # type: ignore[misc]
|
143
153
|
if not isinstance(self.lhs, MultiColSource) and not hasattr(
|
144
154
|
self.rhs, "resolve_expression"
|
145
155
|
):
|
@@ -155,9 +165,11 @@ class RelatedLookupMixin:
|
|
155
165
|
target_field = self.lhs.output_field.path_infos[-1].target_fields[-1]
|
156
166
|
self.rhs = target_field.get_prep_value(self.rhs)
|
157
167
|
|
158
|
-
return super().get_prep_lookup()
|
168
|
+
return super().get_prep_lookup() # type: ignore[misc]
|
159
169
|
|
160
|
-
def as_sql(
|
170
|
+
def as_sql(
|
171
|
+
self, compiler: SQLCompiler, connection: BaseDatabaseWrapper
|
172
|
+
) -> tuple[str, list[Any]]: # type: ignore[misc]
|
161
173
|
if isinstance(self.lhs, MultiColSource):
|
162
174
|
assert self.rhs_is_direct_value()
|
163
175
|
self.rhs = get_normalized_value(self.rhs, self.lhs)
|