summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Fedoseev <fedoseev.sergey@gmail.com>2016-06-07 13:55:27 +0500
committerTim Graham <timograham@gmail.com>2016-06-09 10:19:09 -0400
commit21130ce1a9c4fcbfce4c614be9e5408b43092bf0 (patch)
tree9e4a89131523daf9a3409aff1cc485f36da631d2
parentf6681393d3f53a67b4e0645e8d02f95579d8ae2d (diff)
Fixed #26718 -- Added system check for existence of the fields specified by ForeignKey.to_field.
-rw-r--r--django/db/models/fields/related.py23
-rw-r--r--docs/ref/checks.txt2
-rw-r--r--tests/invalid_models_tests/test_relative_fields.py50
3 files changed, 75 insertions, 0 deletions
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 44e053847a..7caba63c3e 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -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:
diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt
index ac96fbf412..18b6d88add 100644
--- a/docs/ref/checks.txt
+++ b/docs/ref/checks.txt
@@ -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.
diff --git a/tests/invalid_models_tests/test_relative_fields.py b/tests/invalid_models_tests/test_relative_fields.py
index 8b95cf3e18..2534b345c2 100644
--- a/tests/invalid_models_tests/test_relative_fields.py
+++ b/tests/invalid_models_tests/test_relative_fields.py
@@ -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):