summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/models/fields/__init__.py20
-rw-r--r--docs/ref/checks.txt1
-rw-r--r--tests/invalid_models_tests/test_ordinary_fields.py17
-rw-r--r--tests/invalid_models_tests/test_relative_fields.py2
4 files changed, 39 insertions, 1 deletions
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 79a983b8fa..d9e2bac348 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -204,6 +204,7 @@ class Field(RegisterLookupMixin):
errors.extend(self._check_db_index())
errors.extend(self._check_null_allowed_for_primary_keys())
errors.extend(self._check_backend_specific_checks(**kwargs))
+ errors.extend(self._check_validators())
errors.extend(self._check_deprecation_details())
return errors
@@ -302,6 +303,25 @@ class Field(RegisterLookupMixin):
return connections[db].validation.check_field(self, **kwargs)
return []
+ def _check_validators(self):
+ errors = []
+ for i, validator in enumerate(self.validators):
+ if not callable(validator):
+ errors.append(
+ checks.Error(
+ "All 'validators' must be callable.",
+ hint=(
+ "validators[{i}] ({repr}) isn't a function or "
+ "instance of a validator class.".format(
+ i=i, repr=repr(validator),
+ )
+ ),
+ obj=self,
+ id='fields.E008',
+ )
+ )
+ return errors
+
def _check_deprecation_details(self):
if self.system_check_removed_details is not None:
return [
diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt
index d71219f5be..a2066bf216 100644
--- a/docs/ref/checks.txt
+++ b/docs/ref/checks.txt
@@ -154,6 +154,7 @@ Model fields
human readable name)`` tuples.
* **fields.E006**: ``db_index`` must be ``None``, ``True`` or ``False``.
* **fields.E007**: Primary keys must not have ``null=True``.
+* **fields.E008**: All ``validators`` must be callable.
* **fields.E100**: ``AutoField``\s must set primary_key=True.
* **fields.E110**: ``BooleanField``\s do not accept null values.
* **fields.E120**: ``CharField``\s must define a ``max_length`` attribute.
diff --git a/tests/invalid_models_tests/test_ordinary_fields.py b/tests/invalid_models_tests/test_ordinary_fields.py
index f1f1f07766..9b5b57834d 100644
--- a/tests/invalid_models_tests/test_ordinary_fields.py
+++ b/tests/invalid_models_tests/test_ordinary_fields.py
@@ -205,6 +205,23 @@ class CharFieldTests(TestCase):
]
self.assertEqual(errors, expected)
+ def test_bad_validators(self):
+ class Model(models.Model):
+ field = models.CharField(max_length=10, validators=[True])
+
+ field = Model._meta.get_field('field')
+ self.assertEqual(field.check(), [
+ Error(
+ "All 'validators' must be callable.",
+ hint=(
+ "validators[0] (True) isn't a function or instance of a "
+ "validator class."
+ ),
+ obj=field,
+ id='fields.E008',
+ ),
+ ])
+
@unittest.skipUnless(connection.vendor == 'mysql',
"Test valid only for MySQL")
def test_too_long_char_field_under_mysql(self):
diff --git a/tests/invalid_models_tests/test_relative_fields.py b/tests/invalid_models_tests/test_relative_fields.py
index db677bd670..023e201a8a 100644
--- a/tests/invalid_models_tests/test_relative_fields.py
+++ b/tests/invalid_models_tests/test_relative_fields.py
@@ -102,7 +102,7 @@ class RelativeFieldTests(SimpleTestCase):
m2m = models.ManyToManyField(
Model,
null=True,
- validators=[''],
+ validators=[lambda x: x],
limit_choices_to={'name': 'test_name'},
through='ThroughModel',
through_fields=('modelm2m', 'model'),