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.
Files changed (105) hide show
  1. plain/models/CHANGELOG.md +23 -0
  2. plain/models/aggregates.py +42 -19
  3. plain/models/backends/base/base.py +125 -105
  4. plain/models/backends/base/client.py +11 -3
  5. plain/models/backends/base/creation.py +22 -12
  6. plain/models/backends/base/features.py +10 -4
  7. plain/models/backends/base/introspection.py +29 -16
  8. plain/models/backends/base/operations.py +187 -91
  9. plain/models/backends/base/schema.py +267 -165
  10. plain/models/backends/base/validation.py +12 -3
  11. plain/models/backends/ddl_references.py +85 -43
  12. plain/models/backends/mysql/base.py +29 -26
  13. plain/models/backends/mysql/client.py +7 -2
  14. plain/models/backends/mysql/compiler.py +12 -3
  15. plain/models/backends/mysql/creation.py +5 -2
  16. plain/models/backends/mysql/features.py +24 -22
  17. plain/models/backends/mysql/introspection.py +22 -13
  18. plain/models/backends/mysql/operations.py +106 -39
  19. plain/models/backends/mysql/schema.py +48 -24
  20. plain/models/backends/mysql/validation.py +13 -6
  21. plain/models/backends/postgresql/base.py +41 -34
  22. plain/models/backends/postgresql/client.py +7 -2
  23. plain/models/backends/postgresql/creation.py +10 -5
  24. plain/models/backends/postgresql/introspection.py +15 -8
  25. plain/models/backends/postgresql/operations.py +109 -42
  26. plain/models/backends/postgresql/schema.py +85 -46
  27. plain/models/backends/sqlite3/_functions.py +151 -115
  28. plain/models/backends/sqlite3/base.py +37 -23
  29. plain/models/backends/sqlite3/client.py +7 -1
  30. plain/models/backends/sqlite3/creation.py +9 -5
  31. plain/models/backends/sqlite3/features.py +5 -3
  32. plain/models/backends/sqlite3/introspection.py +32 -16
  33. plain/models/backends/sqlite3/operations.py +125 -42
  34. plain/models/backends/sqlite3/schema.py +82 -58
  35. plain/models/backends/utils.py +52 -29
  36. plain/models/backups/cli.py +8 -6
  37. plain/models/backups/clients.py +16 -7
  38. plain/models/backups/core.py +24 -13
  39. plain/models/base.py +113 -74
  40. plain/models/cli.py +94 -63
  41. plain/models/config.py +1 -1
  42. plain/models/connections.py +23 -7
  43. plain/models/constraints.py +65 -47
  44. plain/models/database_url.py +1 -1
  45. plain/models/db.py +6 -2
  46. plain/models/deletion.py +66 -43
  47. plain/models/entrypoints.py +1 -1
  48. plain/models/enums.py +22 -11
  49. plain/models/exceptions.py +23 -8
  50. plain/models/expressions.py +440 -257
  51. plain/models/fields/__init__.py +253 -202
  52. plain/models/fields/json.py +120 -54
  53. plain/models/fields/mixins.py +12 -8
  54. plain/models/fields/related.py +284 -252
  55. plain/models/fields/related_descriptors.py +34 -25
  56. plain/models/fields/related_lookups.py +23 -11
  57. plain/models/fields/related_managers.py +81 -47
  58. plain/models/fields/reverse_related.py +58 -55
  59. plain/models/forms.py +89 -63
  60. plain/models/functions/comparison.py +71 -18
  61. plain/models/functions/datetime.py +79 -29
  62. plain/models/functions/math.py +43 -10
  63. plain/models/functions/mixins.py +24 -7
  64. plain/models/functions/text.py +104 -25
  65. plain/models/functions/window.py +12 -6
  66. plain/models/indexes.py +52 -28
  67. plain/models/lookups.py +228 -153
  68. plain/models/migrations/autodetector.py +86 -43
  69. plain/models/migrations/exceptions.py +7 -3
  70. plain/models/migrations/executor.py +33 -7
  71. plain/models/migrations/graph.py +79 -50
  72. plain/models/migrations/loader.py +45 -22
  73. plain/models/migrations/migration.py +23 -18
  74. plain/models/migrations/operations/base.py +37 -19
  75. plain/models/migrations/operations/fields.py +89 -42
  76. plain/models/migrations/operations/models.py +245 -143
  77. plain/models/migrations/operations/special.py +82 -25
  78. plain/models/migrations/optimizer.py +7 -2
  79. plain/models/migrations/questioner.py +58 -31
  80. plain/models/migrations/recorder.py +18 -11
  81. plain/models/migrations/serializer.py +50 -39
  82. plain/models/migrations/state.py +220 -133
  83. plain/models/migrations/utils.py +29 -13
  84. plain/models/migrations/writer.py +17 -14
  85. plain/models/options.py +63 -56
  86. plain/models/otel.py +16 -6
  87. plain/models/preflight.py +35 -12
  88. plain/models/query.py +323 -228
  89. plain/models/query_utils.py +93 -58
  90. plain/models/registry.py +34 -16
  91. plain/models/sql/compiler.py +146 -97
  92. plain/models/sql/datastructures.py +38 -25
  93. plain/models/sql/query.py +255 -169
  94. plain/models/sql/subqueries.py +32 -21
  95. plain/models/sql/where.py +54 -29
  96. plain/models/test/pytest.py +15 -11
  97. plain/models/test/utils.py +4 -2
  98. plain/models/transaction.py +20 -7
  99. plain/models/utils.py +13 -5
  100. {plain_models-0.49.1.dist-info → plain_models-0.50.0.dist-info}/METADATA +1 -1
  101. plain_models-0.50.0.dist-info/RECORD +122 -0
  102. plain_models-0.49.1.dist-info/RECORD +0 -122
  103. {plain_models-0.49.1.dist-info → plain_models-0.50.0.dist-info}/WHEEL +0 -0
  104. {plain_models-0.49.1.dist-info → plain_models-0.50.0.dist-info}/entry_points.txt +0 -0
  105. {plain_models-0.49.1.dist-info → plain_models-0.50.0.dist-info}/licenses/LICENSE +0 -0
plain/models/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # plain-models changelog
2
2
 
3
+ ## [0.50.0](https://github.com/dropseed/plain/releases/plain-models@0.50.0) (2025-10-06)
4
+
5
+ ### What's changed
6
+
7
+ - Added comprehensive type annotations throughout plain-models, improving IDE support and type checking capabilities ([ea1a7df](https://github.com/dropseed/plain/commit/ea1a7df622), [f49ee32](https://github.com/dropseed/plain/commit/f49ee32a90), [369353f](https://github.com/dropseed/plain/commit/369353f9d6), [13b7d16](https://github.com/dropseed/plain/commit/13b7d16f8d), [e23a0ca](https://github.com/dropseed/plain/commit/e23a0cae7c), [02d8551](https://github.com/dropseed/plain/commit/02d85518f0))
8
+ - The `QuerySet` class is now generic and the `model` parameter is now required in the `__init__` method ([719e792](https://github.com/dropseed/plain/commit/719e792c96))
9
+ - Database wrapper classes have been renamed for consistency: `DatabaseWrapper` classes are now named `MySQLDatabaseWrapper`, `PostgreSQLDatabaseWrapper`, and `SQLiteDatabaseWrapper` ([5a39e85](https://github.com/dropseed/plain/commit/5a39e851e5))
10
+ - The plain-models package now has 100% type annotation coverage and is validated in CI to prevent regressions
11
+
12
+ ### Upgrade instructions
13
+
14
+ - No changes required
15
+
16
+ ## [0.49.2](https://github.com/dropseed/plain/releases/plain-models@0.49.2) (2025-10-02)
17
+
18
+ ### What's changed
19
+
20
+ - Updated dependency to use the latest plain package version
21
+
22
+ ### Upgrade instructions
23
+
24
+ - No changes required
25
+
3
26
  ## [0.49.1](https://github.com/dropseed/plain/releases/plain-models@0.49.1) (2025-09-29)
4
27
 
5
28
  ### What's changed
@@ -1,6 +1,6 @@
1
- """
2
- Classes to represent the definitions of aggregate functions.
3
- """
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from plain.models.exceptions import FieldError, FullResultSet
6
6
  from plain.models.expressions import Case, Func, Star, Value, When
@@ -11,6 +11,12 @@ from plain.models.functions.mixins import (
11
11
  NumericOutputFieldMixin,
12
12
  )
13
13
 
14
+ if TYPE_CHECKING:
15
+ from plain.models.backends.base.base import BaseDatabaseWrapper
16
+ from plain.models.expressions import Expression
17
+ from plain.models.query_utils import Q
18
+ from plain.models.sql.compiler import SQLCompiler
19
+
14
20
  __all__ = [
15
21
  "Aggregate",
16
22
  "Avg",
@@ -33,8 +39,13 @@ class Aggregate(Func):
33
39
  empty_result_set_value = None
34
40
 
35
41
  def __init__(
36
- self, *expressions, distinct=False, filter=None, default=None, **extra
37
- ):
42
+ self,
43
+ *expressions: Any,
44
+ distinct: bool = False,
45
+ filter: Q | Expression | None = None,
46
+ default: Any = None,
47
+ **extra: Any,
48
+ ) -> None:
38
49
  if distinct and not self.allow_distinct:
39
50
  raise TypeError(f"{self.__class__.__name__} does not allow distinct.")
40
51
  if default is not None and self.empty_result_set_value is not None:
@@ -44,23 +55,28 @@ class Aggregate(Func):
44
55
  self.default = default
45
56
  super().__init__(*expressions, **extra)
46
57
 
47
- def get_source_fields(self):
58
+ def get_source_fields(self) -> list[Any]:
48
59
  # Don't return the filter expression since it's not a source field.
49
60
  return [e._output_field_or_none for e in super().get_source_expressions()]
50
61
 
51
- def get_source_expressions(self):
62
+ def get_source_expressions(self) -> list[Expression]:
52
63
  source_expressions = super().get_source_expressions()
53
64
  if self.filter:
54
65
  return source_expressions + [self.filter]
55
66
  return source_expressions
56
67
 
57
- def set_source_expressions(self, exprs):
68
+ def set_source_expressions(self, exprs: list[Expression]) -> list[Expression]:
58
69
  self.filter = self.filter and exprs.pop()
59
70
  return super().set_source_expressions(exprs)
60
71
 
61
72
  def resolve_expression(
62
- self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
63
- ):
73
+ self,
74
+ query: Any = None,
75
+ allow_joins: bool = True,
76
+ reuse: Any = None,
77
+ summarize: bool = False,
78
+ for_save: bool = False,
79
+ ) -> Expression:
64
80
  # Aggregates are not allowed in UPDATE queries, so ignore for_save
65
81
  c = super().resolve_expression(query, allow_joins, reuse, summarize)
66
82
  c.filter = c.filter and c.filter.resolve_expression(
@@ -95,16 +111,21 @@ class Aggregate(Func):
95
111
  return coalesce
96
112
 
97
113
  @property
98
- def default_alias(self):
114
+ def default_alias(self) -> str:
99
115
  expressions = self.get_source_expressions()
100
116
  if len(expressions) == 1 and hasattr(expressions[0], "name"):
101
117
  return f"{expressions[0].name}__{self.name.lower()}"
102
118
  raise TypeError("Complex expressions require an alias")
103
119
 
104
- def get_group_by_cols(self):
120
+ def get_group_by_cols(self) -> list[Any]:
105
121
  return []
106
122
 
107
- def as_sql(self, compiler, connection, **extra_context):
123
+ def as_sql(
124
+ self,
125
+ compiler: SQLCompiler,
126
+ connection: BaseDatabaseWrapper,
127
+ **extra_context: Any,
128
+ ) -> tuple[str, tuple[Any, ...]]:
108
129
  extra_context["distinct"] = "DISTINCT " if self.distinct else ""
109
130
  if self.filter:
110
131
  if connection.features.supports_aggregate_filter_clause:
@@ -135,7 +156,7 @@ class Aggregate(Func):
135
156
  )
136
157
  return super().as_sql(compiler, connection, **extra_context)
137
158
 
138
- def _get_repr_options(self):
159
+ def _get_repr_options(self) -> dict[str, Any]:
139
160
  options = super()._get_repr_options()
140
161
  if self.distinct:
141
162
  options["distinct"] = self.distinct
@@ -157,7 +178,9 @@ class Count(Aggregate):
157
178
  allow_distinct = True
158
179
  empty_result_set_value = 0
159
180
 
160
- def __init__(self, expression, filter=None, **extra):
181
+ def __init__(
182
+ self, expression: Any, filter: Q | Expression | None = None, **extra: Any
183
+ ) -> None:
161
184
  if expression == "*":
162
185
  expression = Star()
163
186
  if isinstance(expression, Star) and filter is not None:
@@ -178,11 +201,11 @@ class Min(Aggregate):
178
201
  class StdDev(NumericOutputFieldMixin, Aggregate):
179
202
  name = "StdDev"
180
203
 
181
- def __init__(self, expression, sample=False, **extra):
204
+ def __init__(self, expression: Any, sample: bool = False, **extra: Any) -> None:
182
205
  self.function = "STDDEV_SAMP" if sample else "STDDEV_POP"
183
206
  super().__init__(expression, **extra)
184
207
 
185
- def _get_repr_options(self):
208
+ def _get_repr_options(self) -> dict[str, Any]:
186
209
  return {**super()._get_repr_options(), "sample": self.function == "STDDEV_SAMP"}
187
210
 
188
211
 
@@ -195,9 +218,9 @@ class Sum(FixDurationInputMixin, Aggregate):
195
218
  class Variance(NumericOutputFieldMixin, Aggregate):
196
219
  name = "Variance"
197
220
 
198
- def __init__(self, expression, sample=False, **extra):
221
+ def __init__(self, expression: Any, sample: bool = False, **extra: Any) -> None:
199
222
  self.function = "VAR_SAMP" if sample else "VAR_POP"
200
223
  super().__init__(expression, **extra)
201
224
 
202
- def _get_repr_options(self):
225
+ def _get_repr_options(self) -> dict[str, Any]:
203
226
  return {**super()._get_repr_options(), "sample": self.function == "VAR_SAMP"}