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
@@ -1,8 +1,14 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  import sys
5
+ from typing import TYPE_CHECKING, Any
3
6
 
4
7
  from plain.runtime import settings
5
8
 
9
+ if TYPE_CHECKING:
10
+ from plain.models.backends.base.base import BaseDatabaseWrapper
11
+
6
12
  # The prefix to put on the default database name when creating
7
13
  # the test database.
8
14
  TEST_DATABASE_PREFIX = "test_"
@@ -14,16 +20,16 @@ class BaseDatabaseCreation:
14
20
  destruction of the test database.
15
21
  """
16
22
 
17
- def __init__(self, connection):
23
+ def __init__(self, connection: BaseDatabaseWrapper):
18
24
  self.connection = connection
19
25
 
20
- def _nodb_cursor(self):
26
+ def _nodb_cursor(self) -> Any:
21
27
  return self.connection._nodb_cursor()
22
28
 
23
- def log(self, msg):
29
+ def log(self, msg: str) -> None:
24
30
  sys.stderr.write(msg + os.linesep)
25
31
 
26
- def create_test_db(self, verbosity=1, prefix=""):
32
+ def create_test_db(self, verbosity: int = 1, prefix: str = "") -> str:
27
33
  """
28
34
  Create a test database, prompting the user for confirmation if the
29
35
  database already exists. Return the name of the test database created.
@@ -67,7 +73,7 @@ class BaseDatabaseCreation:
67
73
 
68
74
  return test_database_name
69
75
 
70
- def set_as_test_mirror(self, primary_settings_dict):
76
+ def set_as_test_mirror(self, primary_settings_dict: dict[str, Any]) -> None:
71
77
  """
72
78
  Set this database up to be used in testing as a mirror of a primary
73
79
  database whose settings are given.
@@ -126,7 +132,7 @@ class BaseDatabaseCreation:
126
132
  # # because constraint checks were disabled.
127
133
  # self.connection.check_constraints(table_names=table_names)
128
134
 
129
- def _get_test_db_name(self, prefix=""):
135
+ def _get_test_db_name(self, prefix: str = "") -> str:
130
136
  """
131
137
  Internal implementation - return the name of the test DB that will be
132
138
  created. Only useful when called from create_test_db() and
@@ -146,10 +152,12 @@ class BaseDatabaseCreation:
146
152
  return self.connection.settings_dict["TEST"]["NAME"]
147
153
  return TEST_DATABASE_PREFIX + self.connection.settings_dict["NAME"]
148
154
 
149
- def _execute_create_test_db(self, cursor, parameters):
155
+ def _execute_create_test_db(self, cursor: Any, parameters: dict[str, str]) -> None:
150
156
  cursor.execute("CREATE DATABASE {dbname} {suffix}".format(**parameters))
151
157
 
152
- def _create_test_db(self, *, test_database_name, verbosity, autoclobber):
158
+ def _create_test_db(
159
+ self, *, test_database_name: str, verbosity: int, autoclobber: bool
160
+ ) -> str:
153
161
  """
154
162
  Internal implementation - create the test db tables.
155
163
  """
@@ -187,7 +195,9 @@ class BaseDatabaseCreation:
187
195
 
188
196
  return test_database_name
189
197
 
190
- def destroy_test_db(self, old_database_name=None, verbosity=1):
198
+ def destroy_test_db(
199
+ self, old_database_name: str | None = None, verbosity: int = 1
200
+ ) -> None:
191
201
  """
192
202
  Destroy a test database, prompting the user for confirmation if the
193
203
  database already exists.
@@ -205,7 +215,7 @@ class BaseDatabaseCreation:
205
215
  settings.DATABASE["NAME"] = old_database_name
206
216
  self.connection.settings_dict["NAME"] = old_database_name
207
217
 
208
- def _destroy_test_db(self, test_database_name, verbosity):
218
+ def _destroy_test_db(self, test_database_name: str, verbosity: int) -> None:
209
219
  """
210
220
  Internal implementation - remove the test db tables.
211
221
  """
@@ -218,13 +228,13 @@ class BaseDatabaseCreation:
218
228
  f"DROP DATABASE {self.connection.ops.quote_name(test_database_name)}"
219
229
  )
220
230
 
221
- def sql_table_creation_suffix(self):
231
+ def sql_table_creation_suffix(self) -> str:
222
232
  """
223
233
  SQL to append to the end of the test table creation statements.
224
234
  """
225
235
  return ""
226
236
 
227
- def test_db_signature(self, prefix=""):
237
+ def test_db_signature(self, prefix: str = "") -> tuple[str, str, str, str]:
228
238
  """
229
239
  Return a tuple with elements of self.connection.settings_dict (a
230
240
  DATABASE setting value) that uniquely identify a database
@@ -1,4 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  from functools import cached_property
4
+ from typing import TYPE_CHECKING, Any
5
+
6
+ if TYPE_CHECKING:
7
+ from plain.models.backends.base.base import BaseDatabaseWrapper
2
8
 
3
9
 
4
10
  class BaseDatabaseFeatures:
@@ -185,16 +191,16 @@ class BaseDatabaseFeatures:
185
191
  # Does the backend support unlimited character columns?
186
192
  supports_unlimited_charfield = False
187
193
 
188
- def __init__(self, connection):
194
+ def __init__(self, connection: BaseDatabaseWrapper):
189
195
  self.connection = connection
190
196
 
191
197
  @cached_property
192
- def supports_explaining_query_execution(self):
198
+ def supports_explaining_query_execution(self) -> bool:
193
199
  """Does this backend support explaining query execution?"""
194
200
  return self.connection.ops.explain_prefix is not None
195
201
 
196
202
  @cached_property
197
- def supports_transactions(self):
203
+ def supports_transactions(self) -> bool:
198
204
  """Confirm support for transactions."""
199
205
  with self.connection.cursor() as cursor:
200
206
  cursor.execute("CREATE TABLE ROLLBACK_TEST (X INT)")
@@ -207,7 +213,7 @@ class BaseDatabaseFeatures:
207
213
  cursor.execute("DROP TABLE ROLLBACK_TEST")
208
214
  return count == 0
209
215
 
210
- def allows_group_by_selected_pks_on_model(self, model):
216
+ def allows_group_by_selected_pks_on_model(self, model: Any) -> bool:
211
217
  if not self.allows_group_by_selected_pks:
212
218
  return False
213
219
  return True
@@ -1,4 +1,11 @@
1
+ from __future__ import annotations
2
+
1
3
  from collections import namedtuple
4
+ from collections.abc import Generator
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ if TYPE_CHECKING:
8
+ from plain.models.backends.base.base import BaseDatabaseWrapper
2
9
 
3
10
  # Structure returned by DatabaseIntrospection.get_table_list()
4
11
  TableInfo = namedtuple("TableInfo", ["name", "type"])
@@ -14,12 +21,12 @@ FieldInfo = namedtuple(
14
21
  class BaseDatabaseIntrospection:
15
22
  """Encapsulate backend-specific introspection utilities."""
16
23
 
17
- data_types_reverse = {}
24
+ data_types_reverse: dict[Any, str] = {}
18
25
 
19
- def __init__(self, connection):
26
+ def __init__(self, connection: BaseDatabaseWrapper) -> None:
20
27
  self.connection = connection
21
28
 
22
- def get_field_type(self, data_type, description):
29
+ def get_field_type(self, data_type: Any, description: Any) -> str:
23
30
  """
24
31
  Hook for a database backend to use the cursor description to
25
32
  match a Plain field type to a database column.
@@ -29,7 +36,7 @@ class BaseDatabaseIntrospection:
29
36
  """
30
37
  return self.data_types_reverse[data_type]
31
38
 
32
- def identifier_converter(self, name):
39
+ def identifier_converter(self, name: str) -> str:
33
40
  """
34
41
  Apply a conversion to the identifier for the purposes of comparison.
35
42
 
@@ -37,7 +44,7 @@ class BaseDatabaseIntrospection:
37
44
  """
38
45
  return name
39
46
 
40
- def table_names(self, cursor=None, include_views=False):
47
+ def table_names(self, cursor: Any = None, include_views: bool = False) -> list[str]:
41
48
  """
42
49
  Return a list of names of all tables that exist in the database.
43
50
  Sort the returned table list by Python's default sorting. Do NOT use
@@ -45,7 +52,7 @@ class BaseDatabaseIntrospection:
45
52
  order between databases.
46
53
  """
47
54
 
48
- def get_names(cursor):
55
+ def get_names(cursor: Any) -> list[str]:
49
56
  return sorted(
50
57
  ti.name
51
58
  for ti in self.get_table_list(cursor)
@@ -57,7 +64,7 @@ class BaseDatabaseIntrospection:
57
64
  return get_names(cursor)
58
65
  return get_names(cursor)
59
66
 
60
- def get_table_list(self, cursor):
67
+ def get_table_list(self, cursor: Any) -> list[TableInfo]:
61
68
  """
62
69
  Return an unsorted list of TableInfo named tuples of all tables and
63
70
  views that exist in the database.
@@ -67,7 +74,7 @@ class BaseDatabaseIntrospection:
67
74
  "method"
68
75
  )
69
76
 
70
- def get_table_description(self, cursor, table_name):
77
+ def get_table_description(self, cursor: Any, table_name: str) -> list[FieldInfo]:
71
78
  """
72
79
  Return a description of the table with the DB-API cursor.description
73
80
  interface.
@@ -77,7 +84,7 @@ class BaseDatabaseIntrospection:
77
84
  "get_table_description() method."
78
85
  )
79
86
 
80
- def get_migratable_models(self):
87
+ def get_migratable_models(self) -> Generator[Any, None, None]:
81
88
  from plain.models import models_registry
82
89
  from plain.packages import packages_registry
83
90
 
@@ -90,7 +97,9 @@ class BaseDatabaseIntrospection:
90
97
  if model._meta.can_migrate(self.connection)
91
98
  )
92
99
 
93
- def plain_table_names(self, only_existing=False, include_views=True):
100
+ def plain_table_names(
101
+ self, only_existing: bool = False, include_views: bool = True
102
+ ) -> list[str]:
94
103
  """
95
104
  Return a list of all table names that have associated Plain models and
96
105
  are in INSTALLED_PACKAGES.
@@ -109,7 +118,7 @@ class BaseDatabaseIntrospection:
109
118
  ]
110
119
  return tables
111
120
 
112
- def sequence_list(self):
121
+ def sequence_list(self) -> list[dict[str, Any]]:
113
122
  """
114
123
  Return a list of information about all DB sequences for all models in
115
124
  all packages.
@@ -124,7 +133,9 @@ class BaseDatabaseIntrospection:
124
133
  )
125
134
  return sequence_list
126
135
 
127
- def get_sequences(self, cursor, table_name, table_fields=()):
136
+ def get_sequences(
137
+ self, cursor: Any, table_name: str, table_fields: tuple[Any, ...] = ()
138
+ ) -> list[dict[str, Any]]:
128
139
  """
129
140
  Return a list of introspected sequences for table_name. Each sequence
130
141
  is a dict: {'table': <table_name>, 'column': <column_name>}. An optional
@@ -135,7 +146,7 @@ class BaseDatabaseIntrospection:
135
146
  "method"
136
147
  )
137
148
 
138
- def get_relations(self, cursor, table_name):
149
+ def get_relations(self, cursor: Any, table_name: str) -> dict[str, tuple[str, str]]:
139
150
  """
140
151
  Return a dictionary of {field_name: (field_name_other_table, other_table)}
141
152
  representing all foreign keys in the given table.
@@ -145,21 +156,23 @@ class BaseDatabaseIntrospection:
145
156
  "get_relations() method."
146
157
  )
147
158
 
148
- def get_primary_key_column(self, cursor, table_name):
159
+ def get_primary_key_column(self, cursor: Any, table_name: str) -> str | None:
149
160
  """
150
161
  Return the name of the primary key column for the given table.
151
162
  """
152
163
  columns = self.get_primary_key_columns(cursor, table_name)
153
164
  return columns[0] if columns else None
154
165
 
155
- def get_primary_key_columns(self, cursor, table_name):
166
+ def get_primary_key_columns(self, cursor: Any, table_name: str) -> list[str] | None:
156
167
  """Return a list of primary key columns for the given table."""
157
168
  for constraint in self.get_constraints(cursor, table_name).values():
158
169
  if constraint["primary_key"]:
159
170
  return constraint["columns"]
160
171
  return None
161
172
 
162
- def get_constraints(self, cursor, table_name):
173
+ def get_constraints(
174
+ self, cursor: Any, table_name: str
175
+ ) -> dict[str, dict[str, Any]]:
163
176
  """
164
177
  Retrieve any constraints or keys (unique, pk, fk, check, index)
165
178
  across one or more columns.