Fixed #31410 -- Added system checks for invalid model field names in UniqueConstraint.

This commit is contained in:
Hasan Ramezani 2020-05-04 23:27:13 +02:00 committed by Mariusz Felisiak
parent 8328811f04
commit 3c7bf39e23
3 changed files with 80 additions and 7 deletions

View File

@ -1926,6 +1926,12 @@ class Model(metaclass=ModelBase):
id='models.W038', id='models.W038',
) )
) )
fields = (
field
for constraint in cls._meta.constraints if isinstance(constraint, UniqueConstraint)
for field in constraint.fields
)
errors.extend(cls._check_local_fields(fields, 'constraints'))
return errors return errors

View File

@ -308,17 +308,17 @@ Models
* **models.E009**: All ``index_together`` elements must be lists or tuples. * **models.E009**: All ``index_together`` elements must be lists or tuples.
* **models.E010**: ``unique_together`` must be a list or tuple. * **models.E010**: ``unique_together`` must be a list or tuple.
* **models.E011**: All ``unique_together`` elements must be lists or tuples. * **models.E011**: All ``unique_together`` elements must be lists or tuples.
* **models.E012**: ``indexes/index_together/unique_together`` refers to the * **models.E012**: ``constraints/indexes/index_together/unique_together``
nonexistent field ``<field name>``. refers to the nonexistent field ``<field name>``.
* **models.E013**: ``indexes/index_together/unique_together`` refers to a * **models.E013**: ``constraints/indexes/index_together/unique_together``
``ManyToManyField`` ``<field name>``, but ``ManyToManyField``\s are not refers to a ``ManyToManyField`` ``<field name>``, but ``ManyToManyField``\s
supported for that option. are not supported for that option.
* **models.E014**: ``ordering`` must be a tuple or list (even if you want to * **models.E014**: ``ordering`` must be a tuple or list (even if you want to
order by only one field). order by only one field).
* **models.E015**: ``ordering`` refers to the nonexistent field, related field, * **models.E015**: ``ordering`` refers to the nonexistent field, related field,
or lookup ``<field name>``. or lookup ``<field name>``.
* **models.E016**: ``indexes/index_together/unique_together`` refers to field * **models.E016**: ``constraints/indexes/index_together/unique_together``
``<field_name>`` which is not local to model ``<model>``. refers to field ``<field_name>`` which is not local to model ``<model>``.
* **models.E017**: Proxy model ``<model>`` contains model fields. * **models.E017**: Proxy model ``<model>`` contains model fields.
* **models.E018**: Autogenerated column name too long for field ``<field>``. * **models.E018**: Autogenerated column name too long for field ``<field>``.
Maximum length is ``<maximum length>`` for database ``<alias>``. Maximum length is ``<maximum length>`` for database ``<alias>``.

View File

@ -1501,3 +1501,70 @@ class ConstraintsTests(TestCase):
] ]
self.assertEqual(Model.check(databases=self.databases), []) self.assertEqual(Model.check(databases=self.databases), [])
def test_unique_constraint_pointing_to_missing_field(self):
class Model(models.Model):
class Meta:
constraints = [models.UniqueConstraint(fields=['missing_field'], name='name')]
self.assertEqual(Model.check(databases=self.databases), [
Error(
"'constraints' refers to the nonexistent field "
"'missing_field'.",
obj=Model,
id='models.E012',
),
])
def test_unique_constraint_pointing_to_m2m_field(self):
class Model(models.Model):
m2m = models.ManyToManyField('self')
class Meta:
constraints = [models.UniqueConstraint(fields=['m2m'], name='name')]
self.assertEqual(Model.check(databases=self.databases), [
Error(
"'constraints' refers to a ManyToManyField 'm2m', but "
"ManyToManyFields are not permitted in 'constraints'.",
obj=Model,
id='models.E013',
),
])
def test_unique_constraint_pointing_to_non_local_field(self):
class Parent(models.Model):
field1 = models.IntegerField()
class Child(Parent):
field2 = models.IntegerField()
class Meta:
constraints = [
models.UniqueConstraint(fields=['field2', 'field1'], name='name'),
]
self.assertEqual(Child.check(databases=self.databases), [
Error(
"'constraints' refers to field 'field1' which is not local to "
"model 'Child'.",
hint='This issue may be caused by multi-table inheritance.',
obj=Child,
id='models.E016',
),
])
def test_unique_constraint_pointing_to_fk(self):
class Target(models.Model):
pass
class Model(models.Model):
fk_1 = models.ForeignKey(Target, models.CASCADE, related_name='target_1')
fk_2 = models.ForeignKey(Target, models.CASCADE, related_name='target_2')
class Meta:
constraints = [
models.UniqueConstraint(fields=['fk_1_id', 'fk_2'], name='name'),
]
self.assertEqual(Model.check(databases=self.databases), [])