From 542b7f6c50df18f2aa201cf1de81577c1bee643c Mon Sep 17 00:00:00 2001 From: Amos Onn Date: Tue, 8 Dec 2015 19:03:31 +0200 Subject: [PATCH] Fixed #25896 -- Fixed state bug in SeparateDatabaseAndState.database_backwards(). --- django/db/migrations/operations/special.py | 2 +- docs/releases/1.8.8.txt | 3 + docs/releases/1.9.1.txt | 3 + tests/migrations/test_operations.py | 76 ++++++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/django/db/migrations/operations/special.py b/django/db/migrations/operations/special.py index e1ce9a07b5..6bbae602db 100644 --- a/django/db/migrations/operations/special.py +++ b/django/db/migrations/operations/special.py @@ -50,7 +50,7 @@ class SeparateDatabaseAndState(Operation): to_state = base_state.clone() for dbop in self.database_operations[:-(pos + 1)]: dbop.state_forwards(app_label, to_state) - from_state = base_state.clone() + from_state = to_state.clone() database_operation.state_forwards(app_label, from_state) database_operation.database_backwards(app_label, schema_editor, from_state, to_state) diff --git a/docs/releases/1.8.8.txt b/docs/releases/1.8.8.txt index bd6309b96e..3b86fefaea 100644 --- a/docs/releases/1.8.8.txt +++ b/docs/releases/1.8.8.txt @@ -27,3 +27,6 @@ Bugfixes * Restored the ability to use custom formats from ``formats.py`` with ``django.utils.formats.get_format()`` and the ``date`` template filter (:ticket:`25812`). + +* Fixed a state bug when migrating a ``SeparateDatabaseAndState`` operation + backwards (:ticket:`25896`). diff --git a/docs/releases/1.9.1.txt b/docs/releases/1.9.1.txt index f56c13e517..98569a14df 100644 --- a/docs/releases/1.9.1.txt +++ b/docs/releases/1.9.1.txt @@ -16,3 +16,6 @@ Bugfixes (:ticket:`25548`). * Fixed a system check crash with nested ``ArrayField``\s (:ticket:`25867`). + +* Fixed a state bug when migrating a ``SeparateDatabaseAndState`` operation + backwards (:ticket:`25896`). diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index c8f1189b3c..7118996ff7 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -1850,6 +1850,82 @@ class OperationTests(OperationTestBase): self.assertEqual(definition[1], []) self.assertEqual(sorted(definition[2]), ["database_operations", "state_operations"]) + def test_separate_database_and_state2(self): + """ + A complex SeparateDatabaseAndState operation: Multiple operations both + for state and database. Verify the state dependencies within each list + and that state ops don't affect the database. + """ + app_label = "test_separatedatabaseandstate2" + project_state = self.set_up_test_model(app_label) + # Create the operation + database_operations = [ + migrations.CreateModel( + "ILovePonies", + [("id", models.AutoField(primary_key=True))], + options={"db_table": "iloveponies"}, + ), + migrations.CreateModel( + "ILoveMorePonies", + [("id", models.AutoField(primary_key=True))], + options={"db_table": "ilovemoreponies"}, + ), + migrations.DeleteModel("ILoveMorePonies"), + migrations.CreateModel( + "ILoveEvenMorePonies", + [("id", models.AutoField(primary_key=True))], + options={"db_table": "iloveevenmoreponies"}, + ), + ] + state_operations = [ + migrations.CreateModel( + "SomethingElse", + [("id", models.AutoField(primary_key=True))], + options={"db_table": "somethingelse"}, + ), + migrations.DeleteModel("SomethingElse"), + migrations.CreateModel( + "SomethingCompletelyDifferent", + [("id", models.AutoField(primary_key=True))], + options={"db_table": "somethingcompletelydifferent"}, + ), + ] + operation = migrations.SeparateDatabaseAndState( + state_operations=state_operations, + database_operations=database_operations, + ) + # Test the state alteration + new_state = project_state.clone() + operation.state_forwards(app_label, new_state) + + def assertModelsAndTables(after_db): + # Check that tables and models exist, or don't, as they should: + self.assertNotIn((app_label, "somethingelse"), new_state.models) + self.assertEqual(len(new_state.models[app_label, "somethingcompletelydifferent"].fields), 1) + self.assertNotIn((app_label, "iloveponiesonies"), new_state.models) + self.assertNotIn((app_label, "ilovemoreponies"), new_state.models) + self.assertNotIn((app_label, "iloveevenmoreponies"), new_state.models) + self.assertTableNotExists("somethingelse") + self.assertTableNotExists("somethingcompletelydifferent") + self.assertTableNotExists("ilovemoreponies") + if after_db: + self.assertTableExists("iloveponies") + self.assertTableExists("iloveevenmoreponies") + else: + self.assertTableNotExists("iloveponies") + self.assertTableNotExists("iloveevenmoreponies") + + assertModelsAndTables(after_db=False) + # Test the database alteration + with connection.schema_editor() as editor: + operation.database_forwards(app_label, editor, project_state, new_state) + assertModelsAndTables(after_db=True) + # And test reversal + self.assertTrue(operation.reversible) + with connection.schema_editor() as editor: + operation.database_backwards(app_label, editor, new_state, project_state) + assertModelsAndTables(after_db=False) + class SwappableOperationTests(OperationTestBase): """