Fixed #26521 -- Validated CreateModel bases, fields and managers for duplicates.

This commit is contained in:
James Robert 2016-04-27 12:43:56 -04:00 committed by Simon Charette
parent 6729b96d8a
commit 417e083e55
3 changed files with 86 additions and 12 deletions

View File

@ -12,6 +12,16 @@ from .fields import (
) )
def _check_for_duplicates(arg_name, objs):
used_vals = set()
for val in objs:
if val in used_vals:
raise ValueError(
'Found duplicate %s in CreateModel operation.' % arg_name
)
used_vals.add(val)
class ModelOperation(Operation): class ModelOperation(Operation):
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
@ -43,6 +53,16 @@ class CreateModel(ModelOperation):
self.bases = bases or (models.Model,) self.bases = bases or (models.Model,)
self.managers = managers or [] self.managers = managers or []
super(CreateModel, self).__init__(name) super(CreateModel, self).__init__(name)
# Sanity-check that there are no duplicated field names, bases, or
# manager names
_check_for_duplicates("field", (name for name, _ in self.fields))
_check_for_duplicates(
"base",
(base._meta.label_lower if isinstance(base, models.base.ModelBase) else base.lower()
for base in self.bases
if base is not models.Model)
)
_check_for_duplicates("manager", (name for name, _ in self.managers))
def deconstruct(self): def deconstruct(self):
kwargs = { kwargs = {

View File

@ -10,7 +10,7 @@ from django.db.transaction import atomic
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
from django.test import override_settings, skipUnlessDBFeature from django.test import override_settings, skipUnlessDBFeature
from .models import FoodManager, FoodQuerySet from .models import FoodManager, FoodQuerySet, UnicodeModel
from .test_base import MigrationTestBase from .test_base import MigrationTestBase
try: try:
@ -200,6 +200,60 @@ class OperationTests(OperationTestBase):
definition = operation.deconstruct() definition = operation.deconstruct()
self.assertNotIn('managers', definition[2]) self.assertNotIn('managers', definition[2])
def test_create_model_with_duplicate_field_name(self):
with self.assertRaisesMessage(ValueError, "Found duplicate field in CreateModel operation."):
migrations.CreateModel(
"Pony",
[
("id", models.AutoField(primary_key=True)),
("pink", models.TextField()),
("pink", models.IntegerField(default=1)),
],
)
def test_create_model_with_duplicate_base(self):
with self.assertRaisesMessage(ValueError, "Found duplicate base in CreateModel operation."):
migrations.CreateModel(
"Pony",
fields=[],
bases=("test_crmo.Pony", "test_crmo.Pony",),
)
with self.assertRaisesMessage(ValueError, "Found duplicate base in CreateModel operation."):
migrations.CreateModel(
"Pony",
fields=[],
bases=(UnicodeModel, UnicodeModel,),
)
with self.assertRaisesMessage(ValueError, "Found duplicate base in CreateModel operation."):
migrations.CreateModel(
"Pony",
fields=[],
bases=("test_crmo.Pony", "test_crmo.pony",),
)
with self.assertRaisesMessage(ValueError, "Found duplicate base in CreateModel operation."):
migrations.CreateModel(
"Pony",
fields=[],
bases=(UnicodeModel, 'migrations.unicodemodel',),
)
with self.assertRaisesMessage(ValueError, "Found duplicate base in CreateModel operation."):
migrations.CreateModel(
"Pony",
fields=[],
bases=(UnicodeModel, 'migrations.UnicodeModel',),
)
def test_create_model_with_duplicate_manager_name(self):
with self.assertRaisesMessage(ValueError, "Found duplicate manager in CreateModel operation."):
migrations.CreateModel(
"Pony",
fields=[],
managers=[
("objects", models.Manager()),
("objects", models.Manager()),
],
)
def test_create_model_with_unique_after(self): def test_create_model_with_unique_after(self):
""" """
Tests the CreateModel operation directly followed by an Tests the CreateModel operation directly followed by an

View File

@ -5,7 +5,7 @@ from django.db.migrations import operations
from django.db.migrations.optimizer import MigrationOptimizer from django.db.migrations.optimizer import MigrationOptimizer
from django.test import SimpleTestCase from django.test import SimpleTestCase
from .models import CustomModelBase, EmptyManager from .models import EmptyManager, UnicodeModel
class OptimizerTests(SimpleTestCase): class OptimizerTests(SimpleTestCase):
@ -71,7 +71,7 @@ class OptimizerTests(SimpleTestCase):
name="Foo", name="Foo",
fields=[("name", models.CharField(max_length=255))], fields=[("name", models.CharField(max_length=255))],
options={'verbose_name': 'Foo'}, options={'verbose_name': 'Foo'},
bases=(CustomModelBase), bases=(UnicodeModel,),
managers=managers, managers=managers,
), ),
migrations.RenameModel("Foo", "Bar"), migrations.RenameModel("Foo", "Bar"),
@ -81,7 +81,7 @@ class OptimizerTests(SimpleTestCase):
"Bar", "Bar",
[("name", models.CharField(max_length=255))], [("name", models.CharField(max_length=255))],
options={'verbose_name': 'Foo'}, options={'verbose_name': 'Foo'},
bases=(CustomModelBase), bases=(UnicodeModel,),
managers=managers, managers=managers,
) )
], ],
@ -237,7 +237,7 @@ class OptimizerTests(SimpleTestCase):
name="Foo", name="Foo",
fields=[("name", models.CharField(max_length=255))], fields=[("name", models.CharField(max_length=255))],
options={'verbose_name': 'Foo'}, options={'verbose_name': 'Foo'},
bases=(CustomModelBase), bases=(UnicodeModel,),
managers=managers, managers=managers,
), ),
migrations.AddField("Foo", "age", models.IntegerField()), migrations.AddField("Foo", "age", models.IntegerField()),
@ -250,7 +250,7 @@ class OptimizerTests(SimpleTestCase):
("age", models.IntegerField()), ("age", models.IntegerField()),
], ],
options={'verbose_name': 'Foo'}, options={'verbose_name': 'Foo'},
bases=(CustomModelBase), bases=(UnicodeModel,),
managers=managers, managers=managers,
), ),
], ],
@ -309,7 +309,7 @@ class OptimizerTests(SimpleTestCase):
name="Foo", name="Foo",
fields=[("name", models.CharField(max_length=255))], fields=[("name", models.CharField(max_length=255))],
options={'verbose_name': 'Foo'}, options={'verbose_name': 'Foo'},
bases=(CustomModelBase), bases=(UnicodeModel,),
managers=managers, managers=managers,
), ),
migrations.AlterField("Foo", "name", models.IntegerField()), migrations.AlterField("Foo", "name", models.IntegerField()),
@ -321,7 +321,7 @@ class OptimizerTests(SimpleTestCase):
("name", models.IntegerField()), ("name", models.IntegerField()),
], ],
options={'verbose_name': 'Foo'}, options={'verbose_name': 'Foo'},
bases=(CustomModelBase), bases=(UnicodeModel,),
managers=managers, managers=managers,
), ),
], ],
@ -338,7 +338,7 @@ class OptimizerTests(SimpleTestCase):
name="Foo", name="Foo",
fields=[("name", models.CharField(max_length=255))], fields=[("name", models.CharField(max_length=255))],
options={'verbose_name': 'Foo'}, options={'verbose_name': 'Foo'},
bases=(CustomModelBase), bases=(UnicodeModel,),
managers=managers, managers=managers,
), ),
migrations.RenameField("Foo", "name", "title"), migrations.RenameField("Foo", "name", "title"),
@ -350,7 +350,7 @@ class OptimizerTests(SimpleTestCase):
("title", models.CharField(max_length=255)), ("title", models.CharField(max_length=255)),
], ],
options={'verbose_name': 'Foo'}, options={'verbose_name': 'Foo'},
bases=(CustomModelBase), bases=(UnicodeModel,),
managers=managers, managers=managers,
), ),
], ],
@ -401,7 +401,7 @@ class OptimizerTests(SimpleTestCase):
("age", models.IntegerField()), ("age", models.IntegerField()),
], ],
options={'verbose_name': 'Foo'}, options={'verbose_name': 'Foo'},
bases=(CustomModelBase), bases=(UnicodeModel,),
managers=managers, managers=managers,
), ),
migrations.RemoveField("Foo", "age"), migrations.RemoveField("Foo", "age"),
@ -413,7 +413,7 @@ class OptimizerTests(SimpleTestCase):
("name", models.CharField(max_length=255)), ("name", models.CharField(max_length=255)),
], ],
options={'verbose_name': 'Foo'}, options={'verbose_name': 'Foo'},
bases=(CustomModelBase), bases=(UnicodeModel,),
managers=managers, managers=managers,
), ),
], ],