plain.models 0.50.0__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 +14 -0
- plain/models/README.md +26 -42
- plain/models/__init__.py +2 -0
- plain/models/backends/base/creation.py +2 -2
- plain/models/backends/base/introspection.py +8 -4
- plain/models/backends/base/schema.py +89 -71
- plain/models/backends/base/validation.py +1 -1
- plain/models/backends/mysql/compiler.py +1 -1
- plain/models/backends/mysql/operations.py +1 -1
- plain/models/backends/mysql/schema.py +4 -4
- plain/models/backends/postgresql/operations.py +1 -1
- plain/models/backends/postgresql/schema.py +3 -3
- plain/models/backends/sqlite3/operations.py +1 -1
- plain/models/backends/sqlite3/schema.py +61 -50
- plain/models/base.py +116 -163
- plain/models/cli.py +4 -4
- plain/models/constraints.py +14 -9
- plain/models/deletion.py +15 -14
- plain/models/expressions.py +1 -1
- plain/models/fields/__init__.py +20 -16
- plain/models/fields/json.py +3 -3
- plain/models/fields/related.py +73 -71
- plain/models/fields/related_descriptors.py +2 -2
- plain/models/fields/related_lookups.py +1 -1
- plain/models/fields/related_managers.py +21 -32
- plain/models/fields/reverse_related.py +8 -8
- plain/models/forms.py +12 -12
- plain/models/indexes.py +5 -4
- plain/models/meta.py +505 -0
- plain/models/migrations/operations/base.py +1 -1
- plain/models/migrations/operations/fields.py +6 -6
- plain/models/migrations/operations/models.py +18 -16
- plain/models/migrations/recorder.py +9 -5
- plain/models/migrations/state.py +35 -46
- plain/models/migrations/utils.py +1 -1
- plain/models/options.py +182 -518
- plain/models/preflight.py +7 -5
- plain/models/query.py +119 -65
- plain/models/query_utils.py +18 -13
- plain/models/registry.py +6 -5
- plain/models/sql/compiler.py +51 -37
- plain/models/sql/query.py +77 -68
- plain/models/sql/subqueries.py +4 -4
- plain/models/utils.py +4 -1
- {plain_models-0.50.0.dist-info → plain_models-0.51.0.dist-info}/METADATA +27 -43
- {plain_models-0.50.0.dist-info → plain_models-0.51.0.dist-info}/RECORD +49 -48
- {plain_models-0.50.0.dist-info → plain_models-0.51.0.dist-info}/WHEEL +0 -0
- {plain_models-0.50.0.dist-info → plain_models-0.51.0.dist-info}/entry_points.txt +0 -0
- {plain_models-0.50.0.dist-info → plain_models-0.51.0.dist-info}/licenses/LICENSE +0 -0
@@ -4,7 +4,7 @@ import copy
|
|
4
4
|
from decimal import Decimal
|
5
5
|
from typing import TYPE_CHECKING, Any
|
6
6
|
|
7
|
-
from plain.models import
|
7
|
+
from plain.models import Options
|
8
8
|
from plain.models.backends.base.schema import BaseDatabaseSchemaEditor
|
9
9
|
from plain.models.backends.ddl_references import Statement
|
10
10
|
from plain.models.backends.utils import strip_quotes
|
@@ -17,6 +17,8 @@ if TYPE_CHECKING:
|
|
17
17
|
from plain.models.base import Model
|
18
18
|
from plain.models.constraints import BaseConstraint
|
19
19
|
from plain.models.fields import Field
|
20
|
+
from plain.models.fields.related import ManyToManyField
|
21
|
+
from plain.models.fields.reverse_related import ManyToManyRel
|
20
22
|
|
21
23
|
|
22
24
|
class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
@@ -139,7 +141,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
139
141
|
if not self._field_should_be_altered(old_field, new_field):
|
140
142
|
return
|
141
143
|
old_field_name = old_field.name
|
142
|
-
table_name = model.
|
144
|
+
table_name = model.model_options.db_table
|
143
145
|
_, old_column_name = old_field.get_attname_column()
|
144
146
|
if (
|
145
147
|
new_field.name != old_field_name
|
@@ -150,7 +152,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
150
152
|
):
|
151
153
|
if self.connection.in_atomic_block:
|
152
154
|
raise NotSupportedError(
|
153
|
-
f"Renaming the {model.
|
155
|
+
f"Renaming the {model.model_options.db_table!r}.{old_field_name!r} column while in a transaction is not "
|
154
156
|
"supported on SQLite < 3.26 because it would break referential "
|
155
157
|
"integrity. Try adding `atomic = False` to the Migration class."
|
156
158
|
)
|
@@ -217,13 +219,13 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
217
219
|
# Work out the new fields dict / mapping
|
218
220
|
body = {
|
219
221
|
f.name: f.clone() if is_self_referential(f) else f
|
220
|
-
for f in model.
|
222
|
+
for f in model._model_meta.local_concrete_fields
|
221
223
|
}
|
222
224
|
# Since mapping might mix column names and default values,
|
223
225
|
# its values must be already quoted.
|
224
226
|
mapping = {
|
225
227
|
f.column: self.quote_name(f.column)
|
226
|
-
for f in model.
|
228
|
+
for f in model._model_meta.local_concrete_fields
|
227
229
|
}
|
228
230
|
# If any of the new or altered fields is introducing a new PK,
|
229
231
|
# remove the old one
|
@@ -270,13 +272,13 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
270
272
|
# Work inside a new app registry
|
271
273
|
models_registry = ModelsRegistry()
|
272
274
|
|
273
|
-
indexes = model.
|
275
|
+
indexes = model.model_options.indexes
|
274
276
|
if delete_field:
|
275
277
|
indexes = [
|
276
278
|
index for index in indexes if delete_field.name not in index.fields
|
277
279
|
]
|
278
280
|
|
279
|
-
constraints = list(model.
|
281
|
+
constraints = list(model.model_options.constraints)
|
280
282
|
|
281
283
|
# Provide isolated instances of the fields to the new model body so
|
282
284
|
# that the existing model's internals aren't interfered with when
|
@@ -288,32 +290,33 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
288
290
|
# table and solely exists for foreign key reference resolution purposes.
|
289
291
|
# This wouldn't be required if the schema editor was operating on model
|
290
292
|
# states instead of rendered models.
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
meta = type("Meta", (), meta_contents)
|
299
|
-
body_copy["Meta"] = meta
|
293
|
+
meta_options = Options(
|
294
|
+
package_label=model.model_options.package_label,
|
295
|
+
db_table=model.model_options.db_table,
|
296
|
+
indexes=indexes,
|
297
|
+
constraints=constraints,
|
298
|
+
)
|
299
|
+
body_copy["model_options"] = meta_options
|
300
300
|
body_copy["__module__"] = model.__module__
|
301
|
-
|
301
|
+
temp_model: type[Model] = type( # type: ignore[assignment]
|
302
|
+
model.model_options.object_name, model.__bases__, body_copy
|
303
|
+
)
|
304
|
+
models_registry.register_model(model.model_options.package_label, temp_model)
|
302
305
|
|
303
306
|
# Construct a model with a renamed table name.
|
304
307
|
body_copy = copy.deepcopy(body)
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
meta = type("Meta", (), meta_contents)
|
313
|
-
body_copy["Meta"] = meta
|
308
|
+
meta_options = Options(
|
309
|
+
package_label=model.model_options.package_label,
|
310
|
+
db_table=f"new__{strip_quotes(model.model_options.db_table)}",
|
311
|
+
indexes=indexes,
|
312
|
+
constraints=constraints,
|
313
|
+
)
|
314
|
+
body_copy["model_options"] = meta_options
|
314
315
|
body_copy["__module__"] = model.__module__
|
315
|
-
new_model = type(
|
316
|
-
|
316
|
+
new_model: type[Model] = type( # type: ignore[assignment]
|
317
|
+
f"New{model.model_options.object_name}", model.__bases__, body_copy
|
318
|
+
)
|
319
|
+
models_registry.register_model(model.model_options.package_label, new_model)
|
317
320
|
|
318
321
|
# Create a new table with the updated schema.
|
319
322
|
self.create_model(new_model)
|
@@ -321,10 +324,10 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
321
324
|
# Copy data from the old table into the new table
|
322
325
|
self.execute(
|
323
326
|
"INSERT INTO {} ({}) SELECT {} FROM {}".format(
|
324
|
-
self.quote_name(new_model.
|
327
|
+
self.quote_name(new_model.model_options.db_table),
|
325
328
|
", ".join(self.quote_name(x) for x in mapping),
|
326
329
|
", ".join(mapping.values()),
|
327
|
-
self.quote_name(model.
|
330
|
+
self.quote_name(model.model_options.db_table),
|
328
331
|
)
|
329
332
|
)
|
330
333
|
|
@@ -334,8 +337,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
334
337
|
# Rename the new table to take way for the old
|
335
338
|
self.alter_db_table(
|
336
339
|
new_model, # type: ignore[arg-type]
|
337
|
-
new_model.
|
338
|
-
model.
|
340
|
+
new_model.model_options.db_table,
|
341
|
+
model.model_options.db_table,
|
339
342
|
disable_constraints=False,
|
340
343
|
)
|
341
344
|
|
@@ -355,13 +358,13 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
355
358
|
self.execute(
|
356
359
|
self.sql_delete_table
|
357
360
|
% {
|
358
|
-
"table": self.quote_name(model.
|
361
|
+
"table": self.quote_name(model.model_options.db_table),
|
359
362
|
}
|
360
363
|
)
|
361
364
|
# Remove all deferred statements referencing the deleted table.
|
362
365
|
for sql in list(self.deferred_sql):
|
363
366
|
if isinstance(sql, Statement) and sql.references_table(
|
364
|
-
model.
|
367
|
+
model.model_options.db_table
|
365
368
|
):
|
366
369
|
self.deferred_sql.remove(sql)
|
367
370
|
|
@@ -434,7 +437,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
434
437
|
):
|
435
438
|
return self.execute(
|
436
439
|
self._rename_field_sql(
|
437
|
-
model.
|
440
|
+
model.model_options.db_table, old_field, new_field, new_type
|
438
441
|
)
|
439
442
|
)
|
440
443
|
# Alter by remaking table
|
@@ -446,8 +449,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
446
449
|
old_type != new_type or old_collation != new_collation
|
447
450
|
):
|
448
451
|
related_models = set()
|
449
|
-
|
450
|
-
for remote_field in
|
452
|
+
meta = new_field.model._model_meta
|
453
|
+
for remote_field in meta.related_objects:
|
451
454
|
# Ignore self-relationship since the table was already rebuilt.
|
452
455
|
if remote_field.related_model == model:
|
453
456
|
continue
|
@@ -455,7 +458,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
455
458
|
if remote_field.field_name == new_field.name:
|
456
459
|
related_models.add(remote_field.related_model)
|
457
460
|
if new_field.primary_key:
|
458
|
-
for many_to_many in
|
461
|
+
for many_to_many in meta.many_to_many:
|
459
462
|
# Ignore self-relationship since the table was already rebuilt.
|
460
463
|
if many_to_many.related_model == model:
|
461
464
|
continue
|
@@ -463,26 +466,34 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
463
466
|
self._remake_table(related_model)
|
464
467
|
|
465
468
|
def _alter_many_to_many(
|
466
|
-
self,
|
469
|
+
self,
|
470
|
+
model: type[Model],
|
471
|
+
old_field: ManyToManyField,
|
472
|
+
new_field: ManyToManyField,
|
473
|
+
strict: bool,
|
467
474
|
) -> None:
|
468
475
|
"""Alter M2Ms to repoint their to= endpoints."""
|
476
|
+
# Type narrow for ManyToManyField.remote_field
|
477
|
+
old_rel: ManyToManyRel = old_field.remote_field # type: ignore[assignment]
|
478
|
+
new_rel: ManyToManyRel = new_field.remote_field # type: ignore[assignment]
|
479
|
+
|
469
480
|
if (
|
470
|
-
|
471
|
-
==
|
481
|
+
old_rel.through.model_options.db_table
|
482
|
+
== new_rel.through.model_options.db_table
|
472
483
|
):
|
473
484
|
# The field name didn't change, but some options did, so we have to
|
474
485
|
# propagate this altering.
|
475
486
|
self._remake_table(
|
476
|
-
|
487
|
+
old_rel.through,
|
477
488
|
alter_fields=[
|
478
489
|
(
|
479
490
|
# The field that points to the target model is needed,
|
480
491
|
# so that table can be remade with the new m2m field -
|
481
492
|
# this is m2m_reverse_field_name().
|
482
|
-
|
493
|
+
old_rel.through._model_meta.get_field(
|
483
494
|
old_field.m2m_reverse_field_name() # type: ignore[attr-defined]
|
484
495
|
),
|
485
|
-
|
496
|
+
new_rel.through._model_meta.get_field(
|
486
497
|
new_field.m2m_reverse_field_name() # type: ignore[attr-defined]
|
487
498
|
),
|
488
499
|
),
|
@@ -490,10 +501,10 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
490
501
|
# The field that points to the model itself is needed,
|
491
502
|
# so that table can be remade with the new self field -
|
492
503
|
# this is m2m_field_name().
|
493
|
-
|
504
|
+
old_rel.through._model_meta.get_field(
|
494
505
|
old_field.m2m_field_name() # type: ignore[attr-defined]
|
495
506
|
),
|
496
|
-
|
507
|
+
new_rel.through._model_meta.get_field(
|
497
508
|
new_field.m2m_field_name() # type: ignore[attr-defined]
|
498
509
|
),
|
499
510
|
),
|
@@ -502,11 +513,11 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
502
513
|
return
|
503
514
|
|
504
515
|
# Make a new through table
|
505
|
-
self.create_model(
|
516
|
+
self.create_model(new_rel.through)
|
506
517
|
# Copy the data across
|
507
518
|
self.execute(
|
508
519
|
"INSERT INTO {} ({}) SELECT {} FROM {}".format(
|
509
|
-
self.quote_name(
|
520
|
+
self.quote_name(new_rel.through.model_options.db_table),
|
510
521
|
", ".join(
|
511
522
|
[
|
512
523
|
"id",
|
@@ -521,11 +532,11 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|
521
532
|
old_field.m2m_reverse_name(), # type: ignore[attr-defined]
|
522
533
|
]
|
523
534
|
),
|
524
|
-
self.quote_name(
|
535
|
+
self.quote_name(old_rel.through.model_options.db_table),
|
525
536
|
)
|
526
537
|
)
|
527
538
|
# Delete the old through table
|
528
|
-
self.delete_model(
|
539
|
+
self.delete_model(old_rel.through)
|
529
540
|
|
530
541
|
def add_constraint(self, model: type[Model], constraint: BaseConstraint) -> None:
|
531
542
|
if isinstance(constraint, UniqueConstraint) and (
|