From 58d0dd9260156067263ea7a2da2685c3cd88e18a Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Thu, 29 Jan 2015 16:57:12 +0100 Subject: [PATCH] Refs #24225 -- Added failing test case for removing a previously added field in migrations When a related field is deleted, the related model must be updated. As unchanged models are shared in migration states, the related model must be re-rendered so that the change applies to a new copy of the related model. Thanks Henrik Heimbuerger for the report. --- tests/migrations/test_state.py | 51 +++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/tests/migrations/test_state.py b/tests/migrations/test_state.py index 7db3565657..ca4e71dce1 100644 --- a/tests/migrations/test_state.py +++ b/tests/migrations/test_state.py @@ -1,6 +1,6 @@ from django.apps.registry import Apps from django.db import models -from django.db.migrations.operations import RemoveField +from django.db.migrations.operations import DeleteModel, RemoveField from django.db.migrations.state import ( InvalidBasesError, ModelState, ProjectState, ) @@ -366,6 +366,53 @@ class StateTests(TestCase): project_state.add_model(ModelState.from_model(B)) self.assertEqual(len(project_state.apps.get_models()), 2) + def test_remove_relations(self): + """ + #24225 - Tests that relations between models are updated while + remaining the relations and references for models of an old state. + """ + class A(models.Model): + class Meta: + app_label = "something" + + class B(models.Model): + to_a = models.ForeignKey(A) + + class Meta: + app_label = "something" + + def get_model_a(state): + return [mod for mod in state.apps.get_models() if mod._meta.model_name == 'a'][0] + + project_state = ProjectState() + project_state.add_model(ModelState.from_model(A)) + project_state.add_model(ModelState.from_model(B)) + self.assertEqual(len(get_model_a(project_state)._meta.related_objects), 1) + old_state = project_state.clone() + + operation = RemoveField("b", "to_a") + operation.state_forwards("something", project_state) + # Tests that model from old_state still has the relation + model_a_old = get_model_a(old_state) + model_a_new = get_model_a(project_state) + self.assertIsNot(model_a_old, model_a_new) + self.assertEqual(len(model_a_old._meta.related_objects), 1) + self.assertEqual(len(model_a_new._meta.related_objects), 0) + + # Same test for deleted model + project_state = ProjectState() + project_state.add_model(ModelState.from_model(A)) + project_state.add_model(ModelState.from_model(B)) + old_state = project_state.clone() + + operation = DeleteModel("b") + operation.state_forwards("something", project_state) + model_a_old = get_model_a(old_state) + model_a_new = get_model_a(project_state) + self.assertIsNot(model_a_old, model_a_new) + self.assertEqual(len(model_a_old._meta.related_objects), 1) + self.assertEqual(len(model_a_new._meta.related_objects), 0) + def test_equality(self): """ Tests that == and != are implemented correctly. @@ -384,11 +431,13 @@ class StateTests(TestCase): {}, None, )) + project_state.apps # Fill the apps cached property other_state = project_state.clone() self.assertEqual(project_state, project_state) self.assertEqual(project_state, other_state) self.assertEqual(project_state != project_state, False) self.assertEqual(project_state != other_state, False) + self.assertNotEqual(project_state.apps, other_state.apps) # Make a very small change (max_len 99) and see if that affects it project_state = ProjectState()