Fixed #26521 -- Validated CreateModel bases, fields and managers for duplicates.
This commit is contained in:
parent
6729b96d8a
commit
417e083e55
|
@ -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 = {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue