Commit 3f1a0082 authored by Andrew Godwin's avatar Andrew Godwin
Browse files

Fixed #21664: Multi-table inheritance was duplicating _ptr fields

parent 1f5268a0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -194,7 +194,7 @@ class MigrationWriter(object):
            if isinstance(value, set):
                format = "set([%s])"
            elif isinstance(value, tuple):
                format = "(%s)" if len(value) else "(%s,)"
                format = "(%s)" if len(value) > 1 else "(%s,)"
            else:
                format = "[%s]"
            return format % (", ".join(strings)), imports
+4 −1
Original line number Diff line number Diff line
@@ -251,6 +251,9 @@ class ModelBase(type):
                    attr_name = '%s_ptr' % base._meta.model_name
                    field = OneToOneField(base, name=attr_name,
                            auto_created=True, parent_link=True)
                    # Only add the ptr field if it's not already present;
                    # e.g. migrations will already have it specified
                    if not hasattr(new_class, attr_name):
                        new_class.add_to_class(attr_name, field)
                else:
                    field = None
+32 −0
Original line number Diff line number Diff line
@@ -90,6 +90,38 @@ class OperationTests(MigrationTestBase):
        self.assertEqual(len(definition[2]), 0)
        self.assertEqual(definition[1][0], "Pony")

    def test_create_model_inheritance(self):
        """
        Tests the CreateModel operation on a multi-table inheritance setup.
        """
        project_state = self.set_up_test_model("test_crmoih")
        # Test the state alteration
        operation = migrations.CreateModel(
            "ShetlandPony",
            [
                ('pony_ptr', models.OneToOneField(
                    auto_created=True,
                    primary_key=True,
                    to_field=u'id',
                    serialize=False,
                    to='test_crmoih.Pony',
                )),
                ("cuteness", models.IntegerField(default=1)),
            ],
        )
        new_state = project_state.clone()
        operation.state_forwards("test_crmoih", new_state)
        self.assertIn(("test_crmoih", "shetlandpony"), new_state.models)
        # Test the database alteration
        self.assertTableNotExists("test_crmoih_shetlandpony")
        with connection.schema_editor() as editor:
            operation.database_forwards("test_crmoih", editor, project_state, new_state)
        self.assertTableExists("test_crmoih_shetlandpony")
        # And test reversal
        with connection.schema_editor() as editor:
            operation.database_backwards("test_crmoih", editor, new_state, project_state)
        self.assertTableNotExists("test_crmoih_shetlandpony")

    def test_delete_model(self):
        """
        Tests the DeleteModel operation.
+30 −1
Original line number Diff line number Diff line
@@ -33,6 +33,12 @@ class StateTests(TestCase):
                proxy = True
                ordering = ["name"]

        class SubAuthor(Author):
            width = models.FloatField(null=True)
            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Book(models.Model):
            title = models.CharField(max_length=1000)
            author = models.ForeignKey(Author)
@@ -47,6 +53,7 @@ class StateTests(TestCase):
        project_state = ProjectState.from_apps(new_apps)
        author_state = project_state.models['migrations', 'author']
        author_proxy_state = project_state.models['migrations', 'authorproxy']
        sub_author_state = project_state.models['migrations', 'subauthor']
        book_state = project_state.models['migrations', 'book']

        self.assertEqual(author_state.app_label, "migrations")
@@ -55,7 +62,7 @@ class StateTests(TestCase):
        self.assertEqual(author_state.fields[1][1].max_length, 255)
        self.assertEqual(author_state.fields[2][1].null, False)
        self.assertEqual(author_state.fields[3][1].null, True)
        self.assertEqual(author_state.options, {"unique_together": {("name", "bio")}})
        self.assertEqual(author_state.options, {"unique_together": set([("name", "bio")])})
        self.assertEqual(author_state.bases, (models.Model, ))

        self.assertEqual(book_state.app_label, "migrations")
@@ -73,6 +80,11 @@ class StateTests(TestCase):
        self.assertEqual(author_proxy_state.options, {"proxy": True, "ordering": ["name"]})
        self.assertEqual(author_proxy_state.bases, ("migrations.author", ))

        self.assertEqual(sub_author_state.app_label, "migrations")
        self.assertEqual(sub_author_state.name, "SubAuthor")
        self.assertEqual(len(sub_author_state.fields), 2)
        self.assertEqual(sub_author_state.bases, ("migrations.author", ))

    def test_render(self):
        """
        Tests rendering a ProjectState into an Apps.
@@ -89,10 +101,27 @@ class StateTests(TestCase):
            {},
            None,
        ))
        project_state.add_model_state(ModelState(
            "migrations",
            "SubTag",
            [
                ('tag_ptr', models.OneToOneField(
                    auto_created=True,
                    primary_key=True,
                    to_field=u'id',
                    serialize=False,
                    to='migrations.Tag',
                )),
                ("awesome", models.BooleanField()),
            ],
            options={},
            bases=("migrations.Tag",),
        ))

        new_apps = project_state.render()
        self.assertEqual(new_apps.get_model("migrations", "Tag")._meta.get_field_by_name("name")[0].max_length, 100)
        self.assertEqual(new_apps.get_model("migrations", "Tag")._meta.get_field_by_name("hidden")[0].null, False)
        self.assertEqual(len(new_apps.get_model("migrations", "SubTag")._meta.local_fields), 2)

    def test_render_model_inheritance(self):
        class Book(models.Model):