1
0
mirror of https://github.com/django/django.git synced 2025-06-25 05:19:13 +00:00

Fixed #35305 -- Avoided recreating constraints on fields renamed via db_column.

This commit is contained in:
Jacob Walls 2025-06-08 16:56:46 -04:00 committed by Sarah Boyce
parent 54402a7529
commit b2407e4d7d
2 changed files with 203 additions and 0 deletions

View File

@ -1460,6 +1460,29 @@ class MigrationAutodetector:
for attr in new_constraint.non_db_attrs:
new_kwargs.pop(attr, None)
# Replace renamed fields if the db_column is preserved.
for (
_,
_,
rem_db_column,
rem_field_name,
_,
_,
field,
field_name,
) in self.renamed_operations:
if field.db_column and rem_db_column == field.db_column:
new_fields = new_kwargs["fields"]
try:
new_field_idx = new_fields.index(field_name)
except ValueError:
continue
new_kwargs["fields"] = tuple(
new_fields[:new_field_idx]
+ (rem_field_name,)
+ new_fields[new_field_idx + 1 :]
)
return (old_path, old_args, old_kwargs) != (new_path, new_args, new_kwargs)
def create_altered_constraints(self):

View File

@ -2078,6 +2078,186 @@ class AutodetectorTests(BaseAutodetectorTests):
new_name="renamed_foo",
)
def test_rename_field_preserve_db_column_preserve_constraint(self):
"""
Renaming a field that already had a db_column attribute and a constraint
generates two no-op operations: RenameField and AlterConstraint.
"""
before = [
ModelState(
"app",
"Foo",
[
("id", models.AutoField(primary_key=True)),
("field", models.IntegerField(db_column="full_field1_name")),
("field2", models.IntegerField()),
],
options={
"constraints": [
models.UniqueConstraint(
fields=["field", "field2"],
name="unique_field",
),
],
},
),
]
after = [
ModelState(
"app",
"Foo",
[
("id", models.AutoField(primary_key=True)),
(
"full_field1_name",
models.IntegerField(db_column="full_field1_name"),
),
(
"field2",
models.IntegerField(),
),
],
options={
"constraints": [
models.UniqueConstraint(
fields=["full_field1_name", "field2"],
name="unique_field",
),
],
},
),
]
changes = self.get_changes(
before, after, MigrationQuestioner({"ask_rename": True})
)
self.assertNumberMigrations(changes, "app", 1)
self.assertOperationTypes(changes, "app", 0, ["RenameField", "AlterConstraint"])
self.assertOperationAttributes(
changes,
"app",
0,
1,
model_name="foo",
name="unique_field",
)
self.assertEqual(
changes["app"][0].operations[1].deconstruct(),
(
"AlterConstraint",
[],
{
"constraint": after[0].options["constraints"][0],
"model_name": "foo",
"name": "unique_field",
},
),
)
def test_rename_field_without_db_column_recreate_constraint(self):
"""Renaming a field without given db_column recreates a constraint."""
before = [
ModelState(
"app",
"Foo",
[
("id", models.AutoField(primary_key=True)),
("field", models.IntegerField()),
],
options={
"constraints": [
models.UniqueConstraint(
fields=["field"],
name="unique_field",
),
],
},
),
]
after = [
ModelState(
"app",
"Foo",
[
("id", models.AutoField(primary_key=True)),
(
"full_field1_name",
models.IntegerField(),
),
],
options={
"constraints": [
models.UniqueConstraint(
fields=["full_field1_name"],
name="unique_field",
),
],
},
),
]
changes = self.get_changes(
before, after, MigrationQuestioner({"ask_rename": True})
)
self.assertNumberMigrations(changes, "app", 1)
self.assertOperationTypes(
changes, "app", 0, ["RemoveConstraint", "RenameField", "AddConstraint"]
)
def test_rename_field_preserve_db_column_recreate_constraint(self):
"""Removing a field from the constraint triggers recreation."""
before = [
ModelState(
"app",
"Foo",
[
("id", models.AutoField(primary_key=True)),
("field1", models.IntegerField(db_column="field1")),
("field2", models.IntegerField(db_column="field2")),
],
options={
"constraints": [
models.UniqueConstraint(
fields=["field1", "field2"],
name="unique_fields",
),
],
},
),
]
after = [
ModelState(
"app",
"Foo",
[
("id", models.AutoField(primary_key=True)),
("renamed_field1", models.IntegerField(db_column="field1")),
("renamed_field2", models.IntegerField(db_column="field2")),
],
options={
"constraints": [
models.UniqueConstraint(
fields=["renamed_field1"],
name="unique_fields",
),
],
},
),
]
changes = self.get_changes(
before, after, MigrationQuestioner({"ask_rename": True})
)
self.assertNumberMigrations(changes, "app", 1)
self.assertOperationTypes(
changes,
"app",
0,
[
"RemoveConstraint",
"RenameField",
"RenameField",
"AddConstraint",
],
)
def test_rename_field_with_renamed_model(self):
changes = self.get_changes(
[self.author_name],