Fixed #34027 -- Fixed migrations crash when altering type of char/text fields referenced by foreign key on PostgreSQL.

This commit is contained in:
David Sanders 2022-09-24 01:40:17 +10:00 committed by Mariusz Felisiak
parent 50096a3a7a
commit 9f8c994851
2 changed files with 53 additions and 12 deletions

View File

@ -131,6 +131,20 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
return "" return ""
def _alter_column_type_sql(self, model, old_field, new_field, new_type): def _alter_column_type_sql(self, model, old_field, new_field, new_type):
# Drop indexes on varchar/text/citext columns that are changing to a
# different type.
old_db_params = old_field.db_parameters(connection=self.connection)
old_type = old_db_params["type"]
if (old_field.db_index or old_field.unique) and (
(old_type.startswith("varchar") and not new_type.startswith("varchar"))
or (old_type.startswith("text") and not new_type.startswith("text"))
or (old_type.startswith("citext") and not new_type.startswith("citext"))
):
index_name = self._create_index_name(
model._meta.db_table, [old_field.column], suffix="_like"
)
self.execute(self._delete_index_sql(model, index_name))
self.sql_alter_column_type = "ALTER COLUMN %(column)s TYPE %(type)s" self.sql_alter_column_type = "ALTER COLUMN %(column)s TYPE %(type)s"
# Cast when data type changed. # Cast when data type changed.
if using_sql := self._using_sql(new_field, old_field): if using_sql := self._using_sql(new_field, old_field):
@ -227,18 +241,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
new_db_params, new_db_params,
strict=False, strict=False,
): ):
# Drop indexes on varchar/text/citext columns that are changing to a
# different type.
if (old_field.db_index or old_field.unique) and (
(old_type.startswith("varchar") and not new_type.startswith("varchar"))
or (old_type.startswith("text") and not new_type.startswith("text"))
or (old_type.startswith("citext") and not new_type.startswith("citext"))
):
index_name = self._create_index_name(
model._meta.db_table, [old_field.column], suffix="_like"
)
self.execute(self._delete_index_sql(model, index_name))
super()._alter_field( super()._alter_field(
model, model,
old_field, old_field,

View File

@ -2436,6 +2436,45 @@ class OperationTests(OperationTestBase):
], ],
) )
def test_alter_field_pk_fk_char_to_int(self):
app_label = "alter_field_pk_fk_char_to_int"
project_state = self.apply_operations(
app_label,
ProjectState(),
operations=[
migrations.CreateModel(
name="Parent",
fields=[
("id", models.CharField(max_length=255, primary_key=True)),
],
),
migrations.CreateModel(
name="Child",
fields=[
("id", models.BigAutoField(primary_key=True)),
(
"parent",
models.ForeignKey(
f"{app_label}.Parent",
on_delete=models.CASCADE,
),
),
],
),
],
)
self.apply_operations(
app_label,
project_state,
operations=[
migrations.AlterField(
model_name="parent",
name="id",
field=models.BigIntegerField(primary_key=True),
),
],
)
def test_rename_field_reloads_state_on_fk_target_changes(self): def test_rename_field_reloads_state_on_fk_target_changes(self):
""" """
If RenameField doesn't reload state appropriately, the AlterField If RenameField doesn't reload state appropriately, the AlterField