Fixed #26718 -- Added system check for existence of the fields specified by ForeignKey.to_field.
This commit is contained in:
parent
f6681393d3
commit
21130ce1a9
|
@ -463,9 +463,32 @@ class ForeignObject(RelatedField):
|
|||
|
||||
def check(self, **kwargs):
|
||||
errors = super(ForeignObject, self).check(**kwargs)
|
||||
errors.extend(self._check_to_fields_exist())
|
||||
errors.extend(self._check_unique_target())
|
||||
return errors
|
||||
|
||||
def _check_to_fields_exist(self):
|
||||
# Skip nonexistent models.
|
||||
if isinstance(self.remote_field.model, six.string_types):
|
||||
return []
|
||||
|
||||
errors = []
|
||||
for to_field in self.to_fields:
|
||||
if to_field:
|
||||
try:
|
||||
self.remote_field.model._meta.get_field(to_field)
|
||||
except exceptions.FieldDoesNotExist:
|
||||
errors.append(
|
||||
checks.Error(
|
||||
"The to_field '%s' doesn't exist on the related "
|
||||
"model '%s'."
|
||||
% (to_field, self.remote_field.model._meta.label),
|
||||
obj=self,
|
||||
id='fields.E312',
|
||||
)
|
||||
)
|
||||
return errors
|
||||
|
||||
def _check_unique_target(self):
|
||||
rel_is_string = isinstance(self.remote_field.model, six.string_types)
|
||||
if rel_is_string or not self.requires_unique_target:
|
||||
|
|
|
@ -211,6 +211,8 @@ Related Fields
|
|||
add at least a subset of them to a unique_together constraint.
|
||||
* **fields.E311**: ``<model>`` must set ``unique=True`` because it is
|
||||
referenced by a ``ForeignKey``.
|
||||
* **fields.E312**: The ``to_field`` ``<field name>`` doesn't exist on the
|
||||
related model ``<app label>.<model>``.
|
||||
* **fields.E320**: Field specifies ``on_delete=SET_NULL``, but cannot be null.
|
||||
* **fields.E321**: The field specifies ``on_delete=SET_DEFAULT``, but has no
|
||||
default value.
|
||||
|
|
|
@ -763,6 +763,56 @@ class RelativeFieldTests(SimpleTestCase):
|
|||
errors = Child.check()
|
||||
self.assertFalse(errors)
|
||||
|
||||
def test_to_fields_exist(self):
|
||||
class Parent(models.Model):
|
||||
pass
|
||||
|
||||
class Child(models.Model):
|
||||
a = models.PositiveIntegerField()
|
||||
b = models.PositiveIntegerField()
|
||||
parent = ForeignObject(
|
||||
Parent,
|
||||
on_delete=models.SET_NULL,
|
||||
from_fields=('a', 'b'),
|
||||
to_fields=('a', 'b'),
|
||||
)
|
||||
|
||||
field = Child._meta.get_field('parent')
|
||||
expected = [
|
||||
Error(
|
||||
"The to_field 'a' doesn't exist on the related model 'invalid_models_tests.Parent'.",
|
||||
obj=field,
|
||||
id='fields.E312',
|
||||
),
|
||||
Error(
|
||||
"The to_field 'b' doesn't exist on the related model 'invalid_models_tests.Parent'.",
|
||||
obj=field,
|
||||
id='fields.E312',
|
||||
),
|
||||
]
|
||||
self.assertEqual(field.check(), expected)
|
||||
|
||||
def test_to_fields_not_checked_if_related_model_doesnt_exist(self):
|
||||
class Child(models.Model):
|
||||
a = models.PositiveIntegerField()
|
||||
b = models.PositiveIntegerField()
|
||||
parent = ForeignObject(
|
||||
'invalid_models_tests.Parent',
|
||||
on_delete=models.SET_NULL,
|
||||
from_fields=('a', 'b'),
|
||||
to_fields=('a', 'b'),
|
||||
)
|
||||
|
||||
field = Child._meta.get_field('parent')
|
||||
self.assertEqual(field.check(), [
|
||||
Error(
|
||||
"Field defines a relation with model 'invalid_models_tests.Parent', "
|
||||
"which is either not installed, or is abstract.",
|
||||
id='fields.E300',
|
||||
obj=field,
|
||||
),
|
||||
])
|
||||
|
||||
|
||||
@isolate_apps('invalid_models_tests')
|
||||
class AccessorClashTests(SimpleTestCase):
|
||||
|
|
Loading…
Reference in New Issue