diff --git a/django/db/backends/postgresql/schema.py b/django/db/backends/postgresql/schema.py index 75bf331472..c0a785c7e1 100644 --- a/django/db/backends/postgresql/schema.py +++ b/django/db/backends/postgresql/schema.py @@ -258,6 +258,25 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): model, old_field, new_field, new_type, old_collation, new_collation ) + def _new_index_should_be_added(self, old_field, new_field): + return not (old_field.db_index or old_field.unique) and ( + new_field.db_index or new_field.unique + ) + + def _deleted_index_should_be_recreated( + self, old_field, new_field, old_type, new_type + ): + if ( + not old_field.unique + and ( + not new_field.db_index + or (new_field.unique and not new_field.primary_key) + ) + ) or ( + self._is_changing_type_of_indexed_text_column(old_field, old_type, new_type) + ): + return True + def _alter_field( self, model, @@ -280,14 +299,10 @@ 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) - or ( - self._is_changing_type_of_indexed_text_column( - old_field, old_type, new_type - ) - ) + if self._new_index_should_be_added( + old_field, new_field + ) or self._deleted_index_should_be_recreated( + old_field, new_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/migrations/test_operations.py b/tests/migrations/test_operations.py index 3ac813b899..318cc527c2 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -6213,6 +6213,42 @@ class OperationTests(OperationTestBase): self.assertEqual(pony_new.static, 2) +class PrimaryKeyOperations(OperationTestBase): + def test_slugfields_change_primary_key_operations(self): + # Create a model with two fields + operation1 = migrations.CreateModel( + "SimpleModel", + [ + ("field1", models.SlugField(max_length=20, primary_key=True)), + ("field2", models.SlugField(max_length=20)), + ], + ) + # Drop field1 primary key constraint - this doesn't fail + operation2 = migrations.AlterField( + "SimpleModel", + "field1", + models.SlugField(max_length=20, primary_key=False), + ) + # Add a primary key constraint to field2 - this fails + operation3 = migrations.AlterField( + "SimpleModel", + "field2", + models.SlugField(max_length=20, primary_key=True), + ) + + project_state = ProjectState() + with connection.schema_editor() as editor: + new_state = project_state.clone() + operation1.state_forwards("migrtest", new_state) + operation1.database_forwards("migrtest", editor, project_state, new_state) + project_state, new_state = new_state, new_state.clone() + operation2.state_forwards("migrtest", new_state) + operation2.database_forwards("migrtest", editor, project_state, new_state) + project_state, new_state = new_state, new_state.clone() + operation3.state_forwards("migrtest", new_state) + operation3.database_forwards("migrtest", editor, project_state, new_state) + + class SwappableOperationTests(OperationTestBase): """ Key operations ignore swappable models