plain.models 0.34.4__tar.gz → 0.36.0__tar.gz
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-0.34.4 → plain_models-0.36.0}/.gitignore +3 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/PKG-INFO +1 -1
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/CHANGELOG.md +22 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/mysql/base.py +5 -48
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/mysql/schema.py +3 -1
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backups/cli.py +2 -4
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/cli.py +36 -114
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/questioner.py +0 -14
- {plain_models-0.34.4 → plain_models-0.36.0}/pyproject.toml +1 -1
- {plain_models-0.34.4 → plain_models-0.36.0}/tests/app/examples/models.py +4 -1
- {plain_models-0.34.4 → plain_models-0.36.0}/tests/app/settings.py +0 -4
- {plain_models-0.34.4 → plain_models-0.36.0}/tests/test_delete_behaviors.py +8 -11
- {plain_models-0.34.4 → plain_models-0.36.0}/LICENSE +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/README.md +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/README.md +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/aggregates.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/base/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/base/base.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/base/client.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/base/creation.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/base/features.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/base/introspection.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/base/operations.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/base/schema.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/base/validation.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/ddl_references.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/mysql/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/mysql/client.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/mysql/compiler.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/mysql/creation.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/mysql/features.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/mysql/introspection.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/mysql/operations.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/mysql/validation.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/postgresql/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/postgresql/base.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/postgresql/client.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/postgresql/creation.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/postgresql/features.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/postgresql/introspection.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/postgresql/operations.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/postgresql/schema.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/sqlite3/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/sqlite3/_functions.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/sqlite3/base.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/sqlite3/client.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/sqlite3/creation.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/sqlite3/features.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/sqlite3/introspection.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/sqlite3/operations.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/sqlite3/schema.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/utils.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backups/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backups/clients.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backups/core.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/base.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/config.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/connections.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/constants.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/constraints.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/database_url.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/db.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/default_settings.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/deletion.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/entrypoints.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/enums.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/exceptions.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/expressions.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/fields/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/fields/json.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/fields/mixins.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/fields/related.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/fields/related_descriptors.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/fields/related_lookups.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/fields/reverse_related.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/forms.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/functions/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/functions/comparison.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/functions/datetime.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/functions/math.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/functions/mixins.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/functions/text.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/functions/window.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/indexes.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/lookups.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/manager.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/autodetector.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/exceptions.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/executor.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/graph.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/loader.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/migration.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/operations/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/operations/base.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/operations/fields.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/operations/models.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/operations/special.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/optimizer.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/recorder.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/serializer.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/state.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/utils.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/migrations/writer.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/options.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/preflight.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/query.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/query_utils.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/registry.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/sql/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/sql/compiler.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/sql/constants.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/sql/datastructures.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/sql/query.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/sql/subqueries.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/sql/where.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/test/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/test/pytest.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/test/utils.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/transaction.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/plain/models/utils.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/tests/app/examples/migrations/0001_initial.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/tests/app/examples/migrations/0002_test_field_removed.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/tests/app/examples/migrations/0003_deleteparent_childsetnull_childsetdefault_and_more.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/tests/app/examples/migrations/__init__.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/tests/app/urls.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/tests/test_database_url.py +0 -0
- {plain_models-0.34.4 → plain_models-0.36.0}/tests/test_models.py +0 -0
@@ -1,5 +1,27 @@
|
|
1
1
|
# plain-models changelog
|
2
2
|
|
3
|
+
## [0.36.0](https://github.com/dropseed/plain/releases/plain-models@0.36.0) (2025-07-18)
|
4
|
+
|
5
|
+
### What's changed
|
6
|
+
|
7
|
+
- Removed the `--merge` option from the `makemigrations` command ([d366663](https://github.com/dropseed/plain/commit/d366663))
|
8
|
+
- Improved error handling in the `restore-backup` command using Click's error system ([88f06c5](https://github.com/dropseed/plain/commit/88f06c5))
|
9
|
+
|
10
|
+
### Upgrade instructions
|
11
|
+
|
12
|
+
- No changes required
|
13
|
+
|
14
|
+
## [0.35.0](https://github.com/dropseed/plain/releases/plain-models@0.35.0) (2025-07-07)
|
15
|
+
|
16
|
+
### What's changed
|
17
|
+
|
18
|
+
- Added the `plain models list` CLI command which prints a nicely formatted list of all installed models, including their table name, fields, and originating package. You can pass package labels to filter the output or use the `--app-only` flag to only show first-party app models ([1bc40ce](https://github.com/dropseed/plain/commit/1bc40ce)).
|
19
|
+
- The MySQL backend no longer enforces a strict `mysqlclient >= 1.4.3` version check and had several unused constraint-handling methods removed, reducing boilerplate and improving compatibility with a wider range of `mysqlclient` versions ([6322400](https://github.com/dropseed/plain/commit/6322400), [67f21f6](https://github.com/dropseed/plain/commit/67f21f6)).
|
20
|
+
|
21
|
+
### Upgrade instructions
|
22
|
+
|
23
|
+
- No changes required
|
24
|
+
|
3
25
|
## [0.34.4](https://github.com/dropseed/plain/releases/plain-models@0.34.4) (2025-07-02)
|
4
26
|
|
5
27
|
### What's changed
|
@@ -6,23 +6,16 @@ Requires mysqlclient: https://pypi.org/project/mysqlclient/
|
|
6
6
|
|
7
7
|
from functools import cached_property
|
8
8
|
|
9
|
+
import MySQLdb as Database
|
10
|
+
from MySQLdb.constants import CLIENT, FIELD_TYPE
|
11
|
+
from MySQLdb.converters import conversions
|
12
|
+
|
9
13
|
from plain.exceptions import ImproperlyConfigured
|
10
14
|
from plain.models.backends import utils as backend_utils
|
11
15
|
from plain.models.backends.base.base import BaseDatabaseWrapper
|
12
16
|
from plain.models.db import IntegrityError
|
13
17
|
from plain.utils.regex_helper import _lazy_re_compile
|
14
18
|
|
15
|
-
try:
|
16
|
-
import MySQLdb as Database
|
17
|
-
except ImportError as err:
|
18
|
-
raise ImproperlyConfigured(
|
19
|
-
"Error loading MySQLdb module.\nDid you install mysqlclient?"
|
20
|
-
) from err
|
21
|
-
|
22
|
-
from MySQLdb.constants import CLIENT, FIELD_TYPE
|
23
|
-
from MySQLdb.converters import conversions
|
24
|
-
|
25
|
-
# Some of these import MySQLdb, so import them after checking if it's installed.
|
26
19
|
from .client import DatabaseClient
|
27
20
|
from .creation import DatabaseCreation
|
28
21
|
from .features import DatabaseFeatures
|
@@ -31,13 +24,6 @@ from .operations import DatabaseOperations
|
|
31
24
|
from .schema import DatabaseSchemaEditor
|
32
25
|
from .validation import DatabaseValidation
|
33
26
|
|
34
|
-
version = Database.version_info
|
35
|
-
if version < (1, 4, 3):
|
36
|
-
raise ImproperlyConfigured(
|
37
|
-
f"mysqlclient 1.4.3 or newer is required; you have {Database.__version__}."
|
38
|
-
)
|
39
|
-
|
40
|
-
|
41
27
|
# MySQLdb returns TIME columns as timedelta -- they are more like timedelta in
|
42
28
|
# terms of actual behavior as they are signed and include days -- and Plain
|
43
29
|
# expects time.
|
@@ -281,37 +267,8 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|
281
267
|
with self.wrap_database_errors:
|
282
268
|
self.connection.autocommit(autocommit)
|
283
269
|
|
284
|
-
def disable_constraint_checking(self):
|
285
|
-
"""
|
286
|
-
Disable foreign key checks, primarily for use in adding rows with
|
287
|
-
forward references. Always return True to indicate constraint checks
|
288
|
-
need to be re-enabled.
|
289
|
-
"""
|
290
|
-
with self.cursor() as cursor:
|
291
|
-
cursor.execute("SET foreign_key_checks=0")
|
292
|
-
return True
|
293
|
-
|
294
|
-
def enable_constraint_checking(self):
|
295
|
-
"""
|
296
|
-
Re-enable foreign key checks after they have been disabled.
|
297
|
-
"""
|
298
|
-
# Override needs_rollback in case constraint_checks_disabled is
|
299
|
-
# nested inside transaction.atomic.
|
300
|
-
self.needs_rollback, needs_rollback = False, self.needs_rollback
|
301
|
-
try:
|
302
|
-
with self.cursor() as cursor:
|
303
|
-
cursor.execute("SET foreign_key_checks=1")
|
304
|
-
finally:
|
305
|
-
self.needs_rollback = needs_rollback
|
306
|
-
|
307
270
|
def check_constraints(self, table_names=None):
|
308
|
-
"""
|
309
|
-
Check each table name in `table_names` for rows with invalid foreign
|
310
|
-
key references. This method is intended to be used in conjunction with
|
311
|
-
`disable_constraint_checking()` and `enable_constraint_checking()`, to
|
312
|
-
determine if rows with invalid references were entered while constraint
|
313
|
-
checks were off.
|
314
|
-
"""
|
271
|
+
"""Check ``table_names`` for rows with invalid foreign key references."""
|
315
272
|
with self.cursor() as cursor:
|
316
273
|
if table_names is None:
|
317
274
|
table_names = self.introspection.table_names(cursor)
|
@@ -1,6 +1,8 @@
|
|
1
1
|
from plain.models.backends.base.schema import BaseDatabaseSchemaEditor
|
2
2
|
from plain.models.constants import LOOKUP_SEP
|
3
|
-
from plain.models.
|
3
|
+
from plain.models.constraints import UniqueConstraint
|
4
|
+
from plain.models.expressions import F
|
5
|
+
from plain.models.fields import NOT_PROVIDED
|
4
6
|
|
5
7
|
|
6
8
|
class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
@@ -66,12 +66,10 @@ def restore_backup(backup_name, latest, pg_restore):
|
|
66
66
|
backups_handler = DatabaseBackups()
|
67
67
|
|
68
68
|
if backup_name and latest:
|
69
|
-
click.
|
70
|
-
exit(1)
|
69
|
+
raise click.UsageError("Only one of --latest or backup_name is allowed")
|
71
70
|
|
72
71
|
if not backup_name and not latest:
|
73
|
-
click.
|
74
|
-
exit(1)
|
72
|
+
raise click.UsageError("Backup name or --latest is required")
|
75
73
|
|
76
74
|
if not backup_name and latest:
|
77
75
|
backup_name = backups_handler.find_backups()[0].name
|
@@ -2,7 +2,6 @@ import os
|
|
2
2
|
import subprocess
|
3
3
|
import sys
|
4
4
|
import time
|
5
|
-
from itertools import takewhile
|
6
5
|
|
7
6
|
import click
|
8
7
|
|
@@ -22,12 +21,10 @@ from .migrations.migration import Migration, SettingsTuple
|
|
22
21
|
from .migrations.optimizer import MigrationOptimizer
|
23
22
|
from .migrations.questioner import (
|
24
23
|
InteractiveMigrationQuestioner,
|
25
|
-
MigrationQuestioner,
|
26
24
|
NonInteractiveMigrationQuestioner,
|
27
25
|
)
|
28
26
|
from .migrations.recorder import MigrationRecorder
|
29
27
|
from .migrations.state import ModelState, ProjectState
|
30
|
-
from .migrations.utils import get_migration_name_timestamp
|
31
28
|
from .migrations.writer import MigrationWriter
|
32
29
|
from .registry import models_registry
|
33
30
|
|
@@ -92,10 +89,41 @@ def db_wait():
|
|
92
89
|
)
|
93
90
|
time.sleep(1.5)
|
94
91
|
else:
|
95
|
-
click.secho("Database ready", fg="green")
|
92
|
+
click.secho("✔ Database ready", fg="green")
|
96
93
|
break
|
97
94
|
|
98
95
|
|
96
|
+
@cli.command(name="list")
|
97
|
+
@click.argument("package_labels", nargs=-1)
|
98
|
+
@click.option(
|
99
|
+
"--app-only",
|
100
|
+
is_flag=True,
|
101
|
+
help="Only show models from packages that start with 'app'.",
|
102
|
+
)
|
103
|
+
def list_models(package_labels, app_only):
|
104
|
+
"""List installed models."""
|
105
|
+
|
106
|
+
packages = set(package_labels)
|
107
|
+
|
108
|
+
for model in sorted(
|
109
|
+
models_registry.get_models(),
|
110
|
+
key=lambda m: (m._meta.package_label, m._meta.model_name),
|
111
|
+
):
|
112
|
+
pkg = model._meta.package_label
|
113
|
+
pkg_name = packages_registry.get_package_config(pkg).name
|
114
|
+
if app_only and not pkg_name.startswith("app"):
|
115
|
+
continue
|
116
|
+
if packages and pkg not in packages:
|
117
|
+
continue
|
118
|
+
fields = ", ".join(f.name for f in model._meta.get_fields())
|
119
|
+
click.echo(
|
120
|
+
f"{click.style(pkg, fg='cyan')}.{click.style(model.__name__, fg='blue')}"
|
121
|
+
)
|
122
|
+
click.echo(f" table: {model._meta.db_table}")
|
123
|
+
click.echo(f" fields: {fields}")
|
124
|
+
click.echo(f" package: {pkg_name}\n")
|
125
|
+
|
126
|
+
|
99
127
|
@register_cli("makemigrations")
|
100
128
|
@cli.command()
|
101
129
|
@click.argument("package_labels", nargs=-1)
|
@@ -104,7 +132,6 @@ def db_wait():
|
|
104
132
|
is_flag=True,
|
105
133
|
help="Just show what migrations would be made; don't actually write them.",
|
106
134
|
)
|
107
|
-
@click.option("--merge", is_flag=True, help="Enable fixing of migration conflicts.")
|
108
135
|
@click.option("--empty", is_flag=True, help="Create an empty migration.")
|
109
136
|
@click.option(
|
110
137
|
"--noinput",
|
@@ -126,9 +153,7 @@ def db_wait():
|
|
126
153
|
default=1,
|
127
154
|
help="Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output",
|
128
155
|
)
|
129
|
-
def makemigrations(
|
130
|
-
package_labels, dry_run, merge, empty, no_input, name, check, verbosity
|
131
|
-
):
|
156
|
+
def makemigrations(package_labels, dry_run, empty, no_input, name, check, verbosity):
|
132
157
|
"""Creates new migration(s) for packages."""
|
133
158
|
|
134
159
|
written_files = []
|
@@ -195,103 +220,6 @@ def makemigrations(
|
|
195
220
|
)
|
196
221
|
log(writer.as_string(), level=3)
|
197
222
|
|
198
|
-
def handle_merge(loader, conflicts):
|
199
|
-
"""Handle merging conflicting migrations."""
|
200
|
-
if interactive:
|
201
|
-
questioner = InteractiveMigrationQuestioner()
|
202
|
-
else:
|
203
|
-
questioner = MigrationQuestioner(defaults={"ask_merge": True})
|
204
|
-
|
205
|
-
for package_label, migration_names in conflicts.items():
|
206
|
-
log(click.style(f"Merging {package_label}", fg="cyan", bold=True), level=1)
|
207
|
-
|
208
|
-
merge_migrations = []
|
209
|
-
for migration_name in migration_names:
|
210
|
-
migration = loader.get_migration(package_label, migration_name)
|
211
|
-
migration.ancestry = [
|
212
|
-
mig
|
213
|
-
for mig in loader.graph.forwards_plan(
|
214
|
-
(package_label, migration_name)
|
215
|
-
)
|
216
|
-
if mig[0] == migration.package_label
|
217
|
-
]
|
218
|
-
merge_migrations.append(migration)
|
219
|
-
|
220
|
-
def all_items_equal(seq):
|
221
|
-
return all(item == seq[0] for item in seq[1:])
|
222
|
-
|
223
|
-
merge_migrations_generations = zip(*(m.ancestry for m in merge_migrations))
|
224
|
-
common_ancestor_count = sum(
|
225
|
-
1 for _ in takewhile(all_items_equal, merge_migrations_generations)
|
226
|
-
)
|
227
|
-
if not common_ancestor_count:
|
228
|
-
raise ValueError(f"Could not find common ancestor of {migration_names}")
|
229
|
-
|
230
|
-
for migration in merge_migrations:
|
231
|
-
migration.branch = migration.ancestry[common_ancestor_count:]
|
232
|
-
migrations_ops = (
|
233
|
-
loader.get_migration(node_package, node_name).operations
|
234
|
-
for node_package, node_name in migration.branch
|
235
|
-
)
|
236
|
-
migration.merged_operations = sum(migrations_ops, [])
|
237
|
-
|
238
|
-
for migration in merge_migrations:
|
239
|
-
log(click.style(f" Branch {migration.name}", fg="yellow"), level=1)
|
240
|
-
for operation in migration.merged_operations:
|
241
|
-
log(f" - {operation.describe()}", level=1)
|
242
|
-
|
243
|
-
if questioner.ask_merge(package_label):
|
244
|
-
numbers = [
|
245
|
-
MigrationAutodetector.parse_number(migration.name)
|
246
|
-
for migration in merge_migrations
|
247
|
-
]
|
248
|
-
biggest_number = (
|
249
|
-
max(x for x in numbers if x is not None) if numbers else 0
|
250
|
-
)
|
251
|
-
|
252
|
-
subclass = type(
|
253
|
-
"Migration",
|
254
|
-
(Migration,),
|
255
|
-
{
|
256
|
-
"dependencies": [
|
257
|
-
(package_label, migration.name)
|
258
|
-
for migration in merge_migrations
|
259
|
-
],
|
260
|
-
},
|
261
|
-
)
|
262
|
-
|
263
|
-
parts = [f"{biggest_number + 1:04d}"]
|
264
|
-
if migration_name:
|
265
|
-
parts.append(migration_name)
|
266
|
-
else:
|
267
|
-
parts.append("merge")
|
268
|
-
leaf_names = "_".join(
|
269
|
-
sorted(migration.name for migration in merge_migrations)
|
270
|
-
)
|
271
|
-
if len(leaf_names) > 47:
|
272
|
-
parts.append(get_migration_name_timestamp())
|
273
|
-
else:
|
274
|
-
parts.append(leaf_names)
|
275
|
-
|
276
|
-
new_migration_name = "_".join(parts)
|
277
|
-
new_migration = subclass(new_migration_name, package_label)
|
278
|
-
writer = MigrationWriter(new_migration)
|
279
|
-
|
280
|
-
if not dry_run:
|
281
|
-
with open(writer.path, "w", encoding="utf-8") as fh:
|
282
|
-
fh.write(writer.as_string())
|
283
|
-
log(f"\nCreated new merge migration {writer.path}", level=1)
|
284
|
-
elif verbosity == 3:
|
285
|
-
log(
|
286
|
-
click.style(
|
287
|
-
f"Full merge migrations file '{writer.filename}':",
|
288
|
-
fg="cyan",
|
289
|
-
bold=True,
|
290
|
-
),
|
291
|
-
level=3,
|
292
|
-
)
|
293
|
-
log(writer.as_string(), level=3)
|
294
|
-
|
295
223
|
# Validate package labels
|
296
224
|
package_labels = set(package_labels)
|
297
225
|
has_bad_labels = False
|
@@ -320,21 +248,16 @@ def makemigrations(
|
|
320
248
|
if package_label in package_labels
|
321
249
|
}
|
322
250
|
|
323
|
-
if conflicts
|
251
|
+
if conflicts:
|
324
252
|
name_str = "; ".join(
|
325
253
|
"{} in {}".format(", ".join(names), package)
|
326
254
|
for package, names in conflicts.items()
|
327
255
|
)
|
328
256
|
raise click.ClickException(
|
329
257
|
f"Conflicting migrations detected; multiple leaf nodes in the "
|
330
|
-
f"migration graph: ({name_str})
|
331
|
-
f"'python manage.py makemigrations --merge'"
|
258
|
+
f"migration graph: ({name_str})."
|
332
259
|
)
|
333
260
|
|
334
|
-
# Handle merge if requested
|
335
|
-
if merge and conflicts:
|
336
|
-
return handle_merge(loader, conflicts)
|
337
|
-
|
338
261
|
# Set up questioner
|
339
262
|
if interactive:
|
340
263
|
questioner = InteractiveMigrationQuestioner(
|
@@ -502,8 +425,7 @@ def migrate(
|
|
502
425
|
)
|
503
426
|
raise click.ClickException(
|
504
427
|
"Conflicting migrations detected; multiple leaf nodes in the "
|
505
|
-
f"migration graph: ({name_str})
|
506
|
-
"'python manage.py makemigrations --merge'"
|
428
|
+
f"migration graph: ({name_str})."
|
507
429
|
)
|
508
430
|
|
509
431
|
# If they supplied command line arguments, work out what they mean.
|
@@ -74,10 +74,6 @@ class MigrationQuestioner:
|
|
74
74
|
"""Was this model really renamed?"""
|
75
75
|
return self.defaults.get("ask_rename_model", False)
|
76
76
|
|
77
|
-
def ask_merge(self, package_label):
|
78
|
-
"""Should these migrations really be merged?"""
|
79
|
-
return self.defaults.get("ask_merge", False)
|
80
|
-
|
81
77
|
def ask_auto_now_add_addition(self, field_name, model_name):
|
82
78
|
"""Adding an auto_now_add field to a model."""
|
83
79
|
# None means quit
|
@@ -227,16 +223,6 @@ class InteractiveMigrationQuestioner(MigrationQuestioner):
|
|
227
223
|
default=False,
|
228
224
|
)
|
229
225
|
|
230
|
-
def ask_merge(self, package_label):
|
231
|
-
return self._boolean_input(
|
232
|
-
(
|
233
|
-
"\nMerging will only work if the operations printed above do not conflict\n"
|
234
|
-
"with each other (working on different fields or models)\n"
|
235
|
-
"Should these migration branches be merged?"
|
236
|
-
),
|
237
|
-
default=False,
|
238
|
-
)
|
239
|
-
|
240
226
|
def ask_auto_now_add_addition(self, field_name, model_name):
|
241
227
|
"""Adding an auto_now_add field to a model."""
|
242
228
|
if not self.dry_run:
|
@@ -47,10 +47,13 @@ class ChildSetNull(models.Model):
|
|
47
47
|
|
48
48
|
@models.register_model
|
49
49
|
class ChildSetDefault(models.Model):
|
50
|
+
def default_parent_pk():
|
51
|
+
return DeleteParent.objects.get(name="default").pk
|
52
|
+
|
50
53
|
parent = models.ForeignKey(
|
51
54
|
DeleteParent,
|
52
55
|
on_delete=models.SET_DEFAULT,
|
53
|
-
default=
|
56
|
+
default=default_parent_pk,
|
54
57
|
)
|
55
58
|
|
56
59
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import pytest
|
2
2
|
from app.examples.models import (
|
3
3
|
ChildCascade,
|
4
|
-
ChildDoNothing,
|
5
4
|
ChildProtect,
|
6
5
|
ChildRestrict,
|
7
6
|
ChildSetDefault,
|
@@ -10,10 +9,8 @@ from app.examples.models import (
|
|
10
9
|
)
|
11
10
|
|
12
11
|
from plain.models import (
|
13
|
-
IntegrityError,
|
14
12
|
ProtectedError,
|
15
13
|
RestrictedError,
|
16
|
-
db_connection,
|
17
14
|
)
|
18
15
|
|
19
16
|
|
@@ -66,11 +63,11 @@ def test_set_default_delete(db):
|
|
66
63
|
assert child.parent_id == default_parent.pk
|
67
64
|
|
68
65
|
|
69
|
-
def test_do_nothing_delete(db):
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
66
|
+
# def test_do_nothing_delete(db):
|
67
|
+
# default_parent, parent = _create_parents()
|
68
|
+
# child = ChildDoNothing.objects.create(parent=parent)
|
69
|
+
# parent.delete()
|
70
|
+
# with pytest.raises(IntegrityError):
|
71
|
+
# db_connection.check_constraints()
|
72
|
+
# child.parent = default_parent
|
73
|
+
# child.save(clean_and_validate=False)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{plain_models-0.34.4 → plain_models-0.36.0}/plain/models/backends/postgresql/introspection.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{plain_models-0.34.4 → plain_models-0.36.0}/tests/app/examples/migrations/0002_test_field_removed.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|