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
@@ -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(
|
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(
|
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(
|
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(
|
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(
|
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.
|