Commit 58d0dd92 authored by Claude Paroz's avatar Claude Paroz Committed by Markus Holtermann
Browse files

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.
parent ad9ecc2c
Loading
Loading
Loading
Loading
+50 −1
Original line number Diff line number Diff line
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()