Loading django/db/backends/schema.py +3 −1 Original line number Diff line number Diff line Loading @@ -546,6 +546,7 @@ class BaseDatabaseSchemaEditor(object): } ) # Drop any FK constraints, we'll remake them later fks_dropped = set() if old_field.rel: fk_names = self._constraint_names(model, [old_field.column], foreign_key=True) if strict and len(fk_names) != 1: Loading @@ -555,6 +556,7 @@ class BaseDatabaseSchemaEditor(object): old_field.column, )) for fk_name in fk_names: fks_dropped.add((old_field.column,)) self.execute( self.sql_delete_fk % { "table": self.quote_name(model._meta.db_table), Loading Loading @@ -737,7 +739,7 @@ class BaseDatabaseSchemaEditor(object): } ) # Does it have a foreign key? if new_field.rel: if new_field.rel and fks_dropped: to_table = new_field.rel.to._meta.db_table to_column = new_field.rel.get_related_field().column self.execute( Loading tests/migrations/test_operations.py +27 −0 Original line number Diff line number Diff line Loading @@ -1051,6 +1051,33 @@ class OperationTests(OperationTestBase): operation.database_backwards("test_alorwrtto", editor, new_state, project_state) self.assertColumnNotExists("test_alorwrtto_rider", "_order") def test_alter_fk(self): """ Tests that creating and then altering an FK works correctly and deals with the pending SQL (#23091) """ project_state = self.set_up_test_model("test_alfk") # Test adding and then altering the FK in one go create_operation = migrations.CreateModel( name="Rider", fields=[ ("id", models.AutoField(primary_key=True)), ("pony", models.ForeignKey(to="Pony")), ], ) create_state = project_state.clone() create_operation.state_forwards("test_alfk", create_state) alter_operation = migrations.AlterField( model_name='Rider', name='pony', field=models.ForeignKey(editable=False, to="Pony"), ) alter_state = create_state.clone() alter_operation.state_forwards("test_alfk", alter_state) with connection.schema_editor() as editor: create_operation.database_forwards("test_alfk", editor, project_state, create_state) alter_operation.database_forwards("test_alfk", editor, create_state, alter_state) @unittest.skipIf(sqlparse is None and connection.features.requires_sqlparse_for_splitting, "Missing sqlparse") def test_run_sql(self): """ Loading tests/schema/tests.py +42 −0 Original line number Diff line number Diff line Loading @@ -331,6 +331,48 @@ class SchemaTests(TransactionTestCase): self.assertEqual(columns['name'][0], "TextField") self.assertEqual(bool(columns['name'][1][6]), False) @unittest.skipUnless(connection.features.supports_foreign_keys, "No FK support") def test_alter_fk(self): """ Tests altering of FKs """ # Create the table with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) # Ensure the field is right to begin with columns = self.column_classes(Book) self.assertEqual(columns['author_id'][0], "IntegerField") # Make sure the FK constraint is present constraints = self.get_constraints(Book._meta.db_table) for name, details in constraints.items(): if details['columns'] == ["author_id"] and details['foreign_key']: self.assertEqual(details['foreign_key'], ('schema_author', 'id')) break else: self.fail("No FK constraint for author_id found") # Alter the FK new_field = ForeignKey(Author, editable=False) new_field.set_attributes_from_name("author") with connection.schema_editor() as editor: editor.alter_field( Book, Book._meta.get_field_by_name("author")[0], new_field, strict=True, ) # Ensure the field is right afterwards columns = self.column_classes(Book) self.assertEqual(columns['author_id'][0], "IntegerField") # Make sure the FK constraint is present constraints = self.get_constraints(Book._meta.db_table) for name, details in constraints.items(): if details['columns'] == ["author_id"] and details['foreign_key']: self.assertEqual(details['foreign_key'], ('schema_author', 'id')) break else: self.fail("No FK constraint for author_id found") def test_rename(self): """ Tests simple altering of fields Loading Loading
django/db/backends/schema.py +3 −1 Original line number Diff line number Diff line Loading @@ -546,6 +546,7 @@ class BaseDatabaseSchemaEditor(object): } ) # Drop any FK constraints, we'll remake them later fks_dropped = set() if old_field.rel: fk_names = self._constraint_names(model, [old_field.column], foreign_key=True) if strict and len(fk_names) != 1: Loading @@ -555,6 +556,7 @@ class BaseDatabaseSchemaEditor(object): old_field.column, )) for fk_name in fk_names: fks_dropped.add((old_field.column,)) self.execute( self.sql_delete_fk % { "table": self.quote_name(model._meta.db_table), Loading Loading @@ -737,7 +739,7 @@ class BaseDatabaseSchemaEditor(object): } ) # Does it have a foreign key? if new_field.rel: if new_field.rel and fks_dropped: to_table = new_field.rel.to._meta.db_table to_column = new_field.rel.get_related_field().column self.execute( Loading
tests/migrations/test_operations.py +27 −0 Original line number Diff line number Diff line Loading @@ -1051,6 +1051,33 @@ class OperationTests(OperationTestBase): operation.database_backwards("test_alorwrtto", editor, new_state, project_state) self.assertColumnNotExists("test_alorwrtto_rider", "_order") def test_alter_fk(self): """ Tests that creating and then altering an FK works correctly and deals with the pending SQL (#23091) """ project_state = self.set_up_test_model("test_alfk") # Test adding and then altering the FK in one go create_operation = migrations.CreateModel( name="Rider", fields=[ ("id", models.AutoField(primary_key=True)), ("pony", models.ForeignKey(to="Pony")), ], ) create_state = project_state.clone() create_operation.state_forwards("test_alfk", create_state) alter_operation = migrations.AlterField( model_name='Rider', name='pony', field=models.ForeignKey(editable=False, to="Pony"), ) alter_state = create_state.clone() alter_operation.state_forwards("test_alfk", alter_state) with connection.schema_editor() as editor: create_operation.database_forwards("test_alfk", editor, project_state, create_state) alter_operation.database_forwards("test_alfk", editor, create_state, alter_state) @unittest.skipIf(sqlparse is None and connection.features.requires_sqlparse_for_splitting, "Missing sqlparse") def test_run_sql(self): """ Loading
tests/schema/tests.py +42 −0 Original line number Diff line number Diff line Loading @@ -331,6 +331,48 @@ class SchemaTests(TransactionTestCase): self.assertEqual(columns['name'][0], "TextField") self.assertEqual(bool(columns['name'][1][6]), False) @unittest.skipUnless(connection.features.supports_foreign_keys, "No FK support") def test_alter_fk(self): """ Tests altering of FKs """ # Create the table with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) # Ensure the field is right to begin with columns = self.column_classes(Book) self.assertEqual(columns['author_id'][0], "IntegerField") # Make sure the FK constraint is present constraints = self.get_constraints(Book._meta.db_table) for name, details in constraints.items(): if details['columns'] == ["author_id"] and details['foreign_key']: self.assertEqual(details['foreign_key'], ('schema_author', 'id')) break else: self.fail("No FK constraint for author_id found") # Alter the FK new_field = ForeignKey(Author, editable=False) new_field.set_attributes_from_name("author") with connection.schema_editor() as editor: editor.alter_field( Book, Book._meta.get_field_by_name("author")[0], new_field, strict=True, ) # Ensure the field is right afterwards columns = self.column_classes(Book) self.assertEqual(columns['author_id'][0], "IntegerField") # Make sure the FK constraint is present constraints = self.get_constraints(Book._meta.db_table) for name, details in constraints.items(): if details['columns'] == ["author_id"] and details['foreign_key']: self.assertEqual(details['foreign_key'], ('schema_author', 'id')) break else: self.fail("No FK constraint for author_id found") def test_rename(self): """ Tests simple altering of fields Loading