From a918c60c378b575ca52ebcfae977056b8bc5a4de Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Mon, 4 Aug 2014 11:58:44 +1000 Subject: [PATCH] [1.7.x] Fixed #23091: CreateModel and AddField were clashing with deferred SQL --- django/db/backends/schema.py | 4 ++- tests/migrations/test_operations.py | 27 +++++++++++++++++++ tests/schema/tests.py | 42 +++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/django/db/backends/schema.py b/django/db/backends/schema.py index c9052e29d8..b10412751d 100644 --- a/django/db/backends/schema.py +++ b/django/db/backends/schema.py @@ -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: @@ -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), @@ -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( diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index cbcc2babbe..518334afa1 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -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): """ diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 0bbf4302c9..64726aff4e 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -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