1
0
mirror of https://github.com/django/django.git synced 2024-12-23 01:25:58 +00:00

Fixed #32263 -- Fixed squashmigrations result with RenameModel on a FK target.

This commit is contained in:
devilsautumn 2023-09-12 21:30:08 +05:30 committed by Sarah Boyce
parent 86e13843c2
commit 2b6b6d5754
4 changed files with 77 additions and 2 deletions

View File

@ -146,6 +146,9 @@ class Operation:
return router.allow_migrate_model(connection_alias, model) return router.allow_migrate_model(connection_alias, model)
def reduce_related(self, operation, app_label):
return None
def reduce(self, operation, app_label): def reduce(self, operation, app_label):
""" """
Return either a list of operations the actual operation should be Return either a list of operations the actual operation should be

View File

@ -134,6 +134,43 @@ class CreateModel(ModelOperation):
return True return True
return False return False
def reduce_related(self, operation, app_label):
if isinstance(operation, RenameModel):
impacted_fields = [
(_, field)
for _, field in self.fields
if field.remote_field
and field.remote_field.model
== f"{app_label}.{operation.old_name_lower}"
]
if len(impacted_fields) == 0:
return [self]
not_impacted_fields = [
(_, field)
for (_, field) in self.fields
if (_, field) not in impacted_fields
]
fixed_fields = []
for _, impacted_field in impacted_fields:
name, path, args, kwargs = impacted_field.deconstruct()
kwargs["to"] = f"{app_label}.{operation.new_name_lower}"
impacted_field = impacted_field.__class__(*args, **kwargs)
fixed_fields.append((_, impacted_field))
return [
CreateModel(
name=self.name,
fields=not_impacted_fields + fixed_fields,
options=self.options,
bases=self.bases,
managers=self.managers,
),
]
return super().reduce_related(operation, app_label)
def reduce(self, operation, app_label): def reduce(self, operation, app_label):
if ( if (
isinstance(operation, DeleteModel) isinstance(operation, DeleteModel)

View File

@ -46,6 +46,7 @@ class MigrationOptimizer:
for j, other in enumerate(operations[i + 1 :]): for j, other in enumerate(operations[i + 1 :]):
result = operation.reduce(other, app_label) result = operation.reduce(other, app_label)
if isinstance(result, list): if isinstance(result, list):
new_reduced_operations = []
in_between = operations[i + 1 : i + j + 1] in_between = operations[i + 1 : i + j + 1]
if right: if right:
new_operations.extend(in_between) new_operations.extend(in_between)
@ -59,8 +60,13 @@ class MigrationOptimizer:
# Otherwise keep trying. # Otherwise keep trying.
new_operations.append(operation) new_operations.append(operation)
break break
new_operations.extend(operations[i + j + 2 :])
return new_operations for _, op in enumerate(new_operations):
new_reduced_operations.extend(
op.reduce_related(other, app_label) or [op]
)
new_reduced_operations.extend(operations[i + j + 2 :])
return new_reduced_operations
elif not result: elif not result:
# Can't perform a right reduction. # Can't perform a right reduction.
right = False right = False

View File

@ -649,6 +649,35 @@ class OptimizerTests(SimpleTestCase):
], ],
) )
def test_rename_model_referenced_by_fk(self):
self.assertOptimizesTo(
[
migrations.CreateModel("Author", []),
migrations.CreateModel(
"Book",
[
(
"author",
models.ForeignKey("migrations.author", models.CASCADE),
),
],
),
migrations.RenameModel("Author", "Person"),
],
[
migrations.CreateModel("Person", []),
migrations.CreateModel(
"Book",
[
(
"author",
models.ForeignKey("migrations.person", models.CASCADE),
),
],
),
],
)
def test_create_model_alter_field(self): def test_create_model_alter_field(self):
""" """
AlterField should optimize into CreateModel. AlterField should optimize into CreateModel.