mirror of
https://github.com/django/django.git
synced 2025-06-02 18:19:11 +00:00
Fixed #35422 -- Fixed migrations crash when altering GeneratedField referencing rename field.
Thanks Sarah Boyce for the report and Simon Charette for the implementation idea.
This commit is contained in:
parent
9aeb38c296
commit
91a4b9a8ec
@ -3,6 +3,7 @@ import operator
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import FieldError
|
||||||
from django.db.backends.ddl_references import (
|
from django.db.backends.ddl_references import (
|
||||||
Columns,
|
Columns,
|
||||||
Expressions,
|
Expressions,
|
||||||
@ -831,6 +832,7 @@ class BaseDatabaseSchemaEditor:
|
|||||||
old_type = old_db_params["type"]
|
old_type = old_db_params["type"]
|
||||||
new_db_params = new_field.db_parameters(connection=self.connection)
|
new_db_params = new_field.db_parameters(connection=self.connection)
|
||||||
new_type = new_db_params["type"]
|
new_type = new_db_params["type"]
|
||||||
|
modifying_generated_field = False
|
||||||
if (old_type is None and old_field.remote_field is None) or (
|
if (old_type is None and old_field.remote_field is None) or (
|
||||||
new_type is None and new_field.remote_field is None
|
new_type is None and new_field.remote_field is None
|
||||||
):
|
):
|
||||||
@ -869,13 +871,19 @@ class BaseDatabaseSchemaEditor:
|
|||||||
"through= on M2M fields)" % (old_field, new_field)
|
"through= on M2M fields)" % (old_field, new_field)
|
||||||
)
|
)
|
||||||
elif old_field.generated != new_field.generated or (
|
elif old_field.generated != new_field.generated or (
|
||||||
new_field.generated
|
new_field.generated and old_field.db_persist != new_field.db_persist
|
||||||
and (
|
|
||||||
old_field.db_persist != new_field.db_persist
|
|
||||||
or old_field.generated_sql(self.connection)
|
|
||||||
!= new_field.generated_sql(self.connection)
|
|
||||||
)
|
|
||||||
):
|
):
|
||||||
|
modifying_generated_field = True
|
||||||
|
elif new_field.generated:
|
||||||
|
try:
|
||||||
|
old_field_sql = old_field.generated_sql(self.connection)
|
||||||
|
except FieldError:
|
||||||
|
# Field used in a generated field was renamed.
|
||||||
|
modifying_generated_field = True
|
||||||
|
else:
|
||||||
|
new_field_sql = new_field.generated_sql(self.connection)
|
||||||
|
modifying_generated_field = old_field_sql != new_field_sql
|
||||||
|
if modifying_generated_field:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Modifying GeneratedFields is not supported - the field {new_field} "
|
f"Modifying GeneratedFields is not supported - the field {new_field} "
|
||||||
"must be removed and re-added with the new definition."
|
"must be removed and re-added with the new definition."
|
||||||
|
@ -158,6 +158,18 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if not self.connection.mysql_is_mariadb:
|
||||||
|
skips.update(
|
||||||
|
{
|
||||||
|
"MySQL doesn't allow renaming columns referenced by generated "
|
||||||
|
"columns": {
|
||||||
|
"migrations.test_operations.OperationTests."
|
||||||
|
"test_invalid_generated_field_changes_on_rename_stored",
|
||||||
|
"migrations.test_operations.OperationTests."
|
||||||
|
"test_invalid_generated_field_changes_on_rename_virtual",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
return skips
|
return skips
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -27,3 +27,6 @@ Bugfixes
|
|||||||
* Fixed a bug in Django 5.0 that caused a migration crash when a
|
* Fixed a bug in Django 5.0 that caused a migration crash when a
|
||||||
``GeneratedField`` was added before any of the referenced fields from its
|
``GeneratedField`` was added before any of the referenced fields from its
|
||||||
``expression`` definition (:ticket:`35359`).
|
``expression`` definition (:ticket:`35359`).
|
||||||
|
|
||||||
|
* Fixed a bug in Django 5.0 that caused a migration crash when altering a
|
||||||
|
``GeneratedField`` referencing a rename field (:ticket:`35422`).
|
||||||
|
@ -5890,6 +5890,50 @@ class OperationTests(OperationTestBase):
|
|||||||
def test_invalid_generated_field_changes_virtual(self):
|
def test_invalid_generated_field_changes_virtual(self):
|
||||||
self._test_invalid_generated_field_changes(db_persist=False)
|
self._test_invalid_generated_field_changes(db_persist=False)
|
||||||
|
|
||||||
|
def _test_invalid_generated_field_changes_on_rename(self, db_persist):
|
||||||
|
app_label = "test_igfcor"
|
||||||
|
operation = migrations.AddField(
|
||||||
|
"Pony",
|
||||||
|
"modified_pink",
|
||||||
|
models.GeneratedField(
|
||||||
|
expression=F("pink") + F("pink"),
|
||||||
|
output_field=models.IntegerField(),
|
||||||
|
db_persist=db_persist,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
project_state, new_state = self.make_test_state(app_label, operation)
|
||||||
|
# Add generated column.
|
||||||
|
with connection.schema_editor() as editor:
|
||||||
|
operation.database_forwards(app_label, editor, project_state, new_state)
|
||||||
|
# Rename field used in the generated field.
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField("Pony", "pink", "renamed_pink"),
|
||||||
|
migrations.AlterField(
|
||||||
|
"Pony",
|
||||||
|
"modified_pink",
|
||||||
|
models.GeneratedField(
|
||||||
|
expression=F("renamed_pink"),
|
||||||
|
output_field=models.IntegerField(),
|
||||||
|
db_persist=db_persist,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
msg = (
|
||||||
|
"Modifying GeneratedFields is not supported - the field "
|
||||||
|
f"{app_label}.Pony.modified_pink must be removed and re-added with the "
|
||||||
|
"new definition."
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(ValueError, msg):
|
||||||
|
self.apply_operations(app_label, new_state, operations)
|
||||||
|
|
||||||
|
@skipUnlessDBFeature("supports_stored_generated_columns")
|
||||||
|
def test_invalid_generated_field_changes_on_rename_stored(self):
|
||||||
|
self._test_invalid_generated_field_changes_on_rename(db_persist=True)
|
||||||
|
|
||||||
|
@skipUnlessDBFeature("supports_virtual_generated_columns")
|
||||||
|
def test_invalid_generated_field_changes_on_rename_virtual(self):
|
||||||
|
self._test_invalid_generated_field_changes_on_rename(db_persist=False)
|
||||||
|
|
||||||
@skipUnlessDBFeature(
|
@skipUnlessDBFeature(
|
||||||
"supports_stored_generated_columns",
|
"supports_stored_generated_columns",
|
||||||
"supports_virtual_generated_columns",
|
"supports_virtual_generated_columns",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user