plain.models 0.49.2__py3-none-any.whl → 0.51.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. plain/models/CHANGELOG.md +27 -0
  2. plain/models/README.md +26 -42
  3. plain/models/__init__.py +2 -0
  4. plain/models/aggregates.py +42 -19
  5. plain/models/backends/base/base.py +125 -105
  6. plain/models/backends/base/client.py +11 -3
  7. plain/models/backends/base/creation.py +24 -14
  8. plain/models/backends/base/features.py +10 -4
  9. plain/models/backends/base/introspection.py +37 -20
  10. plain/models/backends/base/operations.py +187 -91
  11. plain/models/backends/base/schema.py +338 -218
  12. plain/models/backends/base/validation.py +13 -4
  13. plain/models/backends/ddl_references.py +85 -43
  14. plain/models/backends/mysql/base.py +29 -26
  15. plain/models/backends/mysql/client.py +7 -2
  16. plain/models/backends/mysql/compiler.py +13 -4
  17. plain/models/backends/mysql/creation.py +5 -2
  18. plain/models/backends/mysql/features.py +24 -22
  19. plain/models/backends/mysql/introspection.py +22 -13
  20. plain/models/backends/mysql/operations.py +107 -40
  21. plain/models/backends/mysql/schema.py +52 -28
  22. plain/models/backends/mysql/validation.py +13 -6
  23. plain/models/backends/postgresql/base.py +41 -34
  24. plain/models/backends/postgresql/client.py +7 -2
  25. plain/models/backends/postgresql/creation.py +10 -5
  26. plain/models/backends/postgresql/introspection.py +15 -8
  27. plain/models/backends/postgresql/operations.py +110 -43
  28. plain/models/backends/postgresql/schema.py +88 -49
  29. plain/models/backends/sqlite3/_functions.py +151 -115
  30. plain/models/backends/sqlite3/base.py +37 -23
  31. plain/models/backends/sqlite3/client.py +7 -1
  32. plain/models/backends/sqlite3/creation.py +9 -5
  33. plain/models/backends/sqlite3/features.py +5 -3
  34. plain/models/backends/sqlite3/introspection.py +32 -16
  35. plain/models/backends/sqlite3/operations.py +126 -43
  36. plain/models/backends/sqlite3/schema.py +127 -92
  37. plain/models/backends/utils.py +52 -29
  38. plain/models/backups/cli.py +8 -6
  39. plain/models/backups/clients.py +16 -7
  40. plain/models/backups/core.py +24 -13
  41. plain/models/base.py +221 -229
  42. plain/models/cli.py +98 -67
  43. plain/models/config.py +1 -1
  44. plain/models/connections.py +23 -7
  45. plain/models/constraints.py +79 -56
  46. plain/models/database_url.py +1 -1
  47. plain/models/db.py +6 -2
  48. plain/models/deletion.py +80 -56
  49. plain/models/entrypoints.py +1 -1
  50. plain/models/enums.py +22 -11
  51. plain/models/exceptions.py +23 -8
  52. plain/models/expressions.py +441 -258
  53. plain/models/fields/__init__.py +272 -217
  54. plain/models/fields/json.py +123 -57
  55. plain/models/fields/mixins.py +12 -8
  56. plain/models/fields/related.py +324 -290
  57. plain/models/fields/related_descriptors.py +33 -24
  58. plain/models/fields/related_lookups.py +24 -12
  59. plain/models/fields/related_managers.py +102 -79
  60. plain/models/fields/reverse_related.py +66 -63
  61. plain/models/forms.py +101 -75
  62. plain/models/functions/comparison.py +71 -18
  63. plain/models/functions/datetime.py +79 -29
  64. plain/models/functions/math.py +43 -10
  65. plain/models/functions/mixins.py +24 -7
  66. plain/models/functions/text.py +104 -25
  67. plain/models/functions/window.py +12 -6
  68. plain/models/indexes.py +57 -32
  69. plain/models/lookups.py +228 -153
  70. plain/models/meta.py +505 -0
  71. plain/models/migrations/autodetector.py +86 -43
  72. plain/models/migrations/exceptions.py +7 -3
  73. plain/models/migrations/executor.py +33 -7
  74. plain/models/migrations/graph.py +79 -50
  75. plain/models/migrations/loader.py +45 -22
  76. plain/models/migrations/migration.py +23 -18
  77. plain/models/migrations/operations/base.py +38 -20
  78. plain/models/migrations/operations/fields.py +95 -48
  79. plain/models/migrations/operations/models.py +246 -142
  80. plain/models/migrations/operations/special.py +82 -25
  81. plain/models/migrations/optimizer.py +7 -2
  82. plain/models/migrations/questioner.py +58 -31
  83. plain/models/migrations/recorder.py +27 -16
  84. plain/models/migrations/serializer.py +50 -39
  85. plain/models/migrations/state.py +232 -156
  86. plain/models/migrations/utils.py +30 -14
  87. plain/models/migrations/writer.py +17 -14
  88. plain/models/options.py +189 -518
  89. plain/models/otel.py +16 -6
  90. plain/models/preflight.py +42 -17
  91. plain/models/query.py +400 -251
  92. plain/models/query_utils.py +109 -69
  93. plain/models/registry.py +40 -21
  94. plain/models/sql/compiler.py +190 -127
  95. plain/models/sql/datastructures.py +38 -25
  96. plain/models/sql/query.py +320 -225
  97. plain/models/sql/subqueries.py +36 -25
  98. plain/models/sql/where.py +54 -29
  99. plain/models/test/pytest.py +15 -11
  100. plain/models/test/utils.py +4 -2
  101. plain/models/transaction.py +20 -7
  102. plain/models/utils.py +17 -6
  103. {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/METADATA +27 -43
  104. plain_models-0.51.0.dist-info/RECORD +123 -0
  105. plain_models-0.49.2.dist-info/RECORD +0 -122
  106. {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/WHEEL +0 -0
  107. {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/entry_points.txt +0 -0
  108. {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/licenses/LICENSE +0 -0
@@ -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._meta.base_queryset
107
+ qs = self.field.remote_field.model._model_meta.base_queryset
105
108
  return qs.all()
106
109
 
107
- def get_prefetch_queryset(self, instances, queryset=None):
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__(self, instance, cls=None):
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._meta.object_name}.{self.field.name}" must be a "{self.field.remote_field.model._meta.object_name}" 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__(self, instance, cls=None):
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._meta.get_field(
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(self, compiler, connection):
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(self, compiler, connection):
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)