From 0eaaadd47fa00baabe12be3ed736aa016b6d327e Mon Sep 17 00:00:00 2001 From: Ben Cail Date: Tue, 17 Sep 2024 15:10:39 -0400 Subject: [PATCH] Fixed #35180 -- Recreated PostgreSQL _like indexes when changing between TextField and CharField field types. --- django/db/backends/postgresql/schema.py | 23 +++++++++---- tests/schema/tests.py | 45 +++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/django/db/backends/postgresql/schema.py b/django/db/backends/postgresql/schema.py index 0c8548a5d6..75bf331472 100644 --- a/django/db/backends/postgresql/schema.py +++ b/django/db/backends/postgresql/schema.py @@ -140,6 +140,13 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): return sequence["name"] return None + def _is_changing_type_of_indexed_text_column(self, old_field, old_type, new_type): + return (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")) + ) + def _alter_column_type_sql( self, model, old_field, new_field, new_type, old_collation, new_collation ): @@ -147,11 +154,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): # 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")) - ): + if self._is_changing_type_of_indexed_text_column(old_field, old_type, new_type): index_name = self._create_index_name( model._meta.db_table, [old_field.column], suffix="_like" ) @@ -277,8 +280,14 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): strict, ) # Added an index? Create any PostgreSQL-specific indexes. - if (not (old_field.db_index or old_field.unique) and new_field.db_index) or ( - not old_field.unique and new_field.unique + if ( + (not (old_field.db_index or old_field.unique) and new_field.db_index) + or (not old_field.unique and new_field.unique) + or ( + self._is_changing_type_of_indexed_text_column( + old_field, old_type, new_type + ) + ) ): like_index_statement = self._create_like_index_sql(model, new_field) if like_index_statement is not None: diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 33a4bc527b..935267c2d6 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -5223,6 +5223,51 @@ class SchemaTests(TransactionTestCase): ["schema_tag_slug_2c418ba3_like", "schema_tag_slug_key"], ) + @isolate_apps("schema") + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") + def test_indexed_charfield_to_textfield(self): + class SimpleModel(Model): + field1 = CharField(max_length=10, db_index=True) + + class Meta: + app_label = "schema" + + with connection.schema_editor() as editor: + editor.create_model(SimpleModel) + self.assertEqual( + self.get_constraints_for_column(SimpleModel, "field1"), + [ + "schema_simplemodel_field1_f07a3d6a", + "schema_simplemodel_field1_f07a3d6a_like", + ], + ) + # Change to TextField. + old_field1 = SimpleModel._meta.get_field("field1") + new_field1 = TextField(db_index=True) + new_field1.set_attributes_from_name("field1") + with connection.schema_editor() as editor: + editor.alter_field(SimpleModel, old_field1, new_field1, strict=True) + self.assertEqual( + self.get_constraints_for_column(SimpleModel, "field1"), + [ + "schema_simplemodel_field1_f07a3d6a", + "schema_simplemodel_field1_f07a3d6a_like", + ], + ) + # Change back to CharField. + old_field1 = SimpleModel._meta.get_field("field1") + new_field1 = CharField(max_length=10, db_index=True) + new_field1.set_attributes_from_name("field1") + with connection.schema_editor() as editor: + editor.alter_field(SimpleModel, old_field1, new_field1, strict=True) + self.assertEqual( + self.get_constraints_for_column(SimpleModel, "field1"), + [ + "schema_simplemodel_field1_f07a3d6a", + "schema_simplemodel_field1_f07a3d6a_like", + ], + ) + def test_alter_field_add_index_to_integerfield(self): # Create the table and verify no initial indexes. with connection.schema_editor() as editor: