Fixed #31410 -- Added system checks for invalid model field names in UniqueConstraint.
This commit is contained in:
parent
8328811f04
commit
3c7bf39e23
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>``.
|
||||||
|
|
|
@ -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), [])
|
||||||
|
|
Loading…
Reference in New Issue