Fixed #21664: Multi-table inheritance was duplicating _ptr fields
This commit is contained in:
parent
1f5268a01f
commit
3f1a008266
|
@ -194,7 +194,7 @@ class MigrationWriter(object):
|
||||||
if isinstance(value, set):
|
if isinstance(value, set):
|
||||||
format = "set([%s])"
|
format = "set([%s])"
|
||||||
elif isinstance(value, tuple):
|
elif isinstance(value, tuple):
|
||||||
format = "(%s)" if len(value) else "(%s,)"
|
format = "(%s)" if len(value) > 1 else "(%s,)"
|
||||||
else:
|
else:
|
||||||
format = "[%s]"
|
format = "[%s]"
|
||||||
return format % (", ".join(strings)), imports
|
return format % (", ".join(strings)), imports
|
||||||
|
|
|
@ -251,6 +251,9 @@ class ModelBase(type):
|
||||||
attr_name = '%s_ptr' % base._meta.model_name
|
attr_name = '%s_ptr' % base._meta.model_name
|
||||||
field = OneToOneField(base, name=attr_name,
|
field = OneToOneField(base, name=attr_name,
|
||||||
auto_created=True, parent_link=True)
|
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)
|
new_class.add_to_class(attr_name, field)
|
||||||
else:
|
else:
|
||||||
field = None
|
field = None
|
||||||
|
|
|
@ -90,6 +90,38 @@ class OperationTests(MigrationTestBase):
|
||||||
self.assertEqual(len(definition[2]), 0)
|
self.assertEqual(len(definition[2]), 0)
|
||||||
self.assertEqual(definition[1][0], "Pony")
|
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):
|
def test_delete_model(self):
|
||||||
"""
|
"""
|
||||||
Tests the DeleteModel operation.
|
Tests the DeleteModel operation.
|
||||||
|
|
|
@ -33,6 +33,12 @@ class StateTests(TestCase):
|
||||||
proxy = True
|
proxy = True
|
||||||
ordering = ["name"]
|
ordering = ["name"]
|
||||||
|
|
||||||
|
class SubAuthor(Author):
|
||||||
|
width = models.FloatField(null=True)
|
||||||
|
class Meta:
|
||||||
|
app_label = "migrations"
|
||||||
|
apps = new_apps
|
||||||
|
|
||||||
class Book(models.Model):
|
class Book(models.Model):
|
||||||
title = models.CharField(max_length=1000)
|
title = models.CharField(max_length=1000)
|
||||||
author = models.ForeignKey(Author)
|
author = models.ForeignKey(Author)
|
||||||
|
@ -47,6 +53,7 @@ class StateTests(TestCase):
|
||||||
project_state = ProjectState.from_apps(new_apps)
|
project_state = ProjectState.from_apps(new_apps)
|
||||||
author_state = project_state.models['migrations', 'author']
|
author_state = project_state.models['migrations', 'author']
|
||||||
author_proxy_state = project_state.models['migrations', 'authorproxy']
|
author_proxy_state = project_state.models['migrations', 'authorproxy']
|
||||||
|
sub_author_state = project_state.models['migrations', 'subauthor']
|
||||||
book_state = project_state.models['migrations', 'book']
|
book_state = project_state.models['migrations', 'book']
|
||||||
|
|
||||||
self.assertEqual(author_state.app_label, "migrations")
|
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[1][1].max_length, 255)
|
||||||
self.assertEqual(author_state.fields[2][1].null, False)
|
self.assertEqual(author_state.fields[2][1].null, False)
|
||||||
self.assertEqual(author_state.fields[3][1].null, True)
|
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(author_state.bases, (models.Model, ))
|
||||||
|
|
||||||
self.assertEqual(book_state.app_label, "migrations")
|
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.options, {"proxy": True, "ordering": ["name"]})
|
||||||
self.assertEqual(author_proxy_state.bases, ("migrations.author", ))
|
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):
|
def test_render(self):
|
||||||
"""
|
"""
|
||||||
Tests rendering a ProjectState into an Apps.
|
Tests rendering a ProjectState into an Apps.
|
||||||
|
@ -89,10 +101,27 @@ class StateTests(TestCase):
|
||||||
{},
|
{},
|
||||||
None,
|
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()
|
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("name")[0].max_length, 100)
|
||||||
self.assertEqual(new_apps.get_model("migrations", "Tag")._meta.get_field_by_name("hidden")[0].null, False)
|
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):
|
def test_render_model_inheritance(self):
|
||||||
class Book(models.Model):
|
class Book(models.Model):
|
||||||
|
|
Loading…
Reference in New Issue