mirror of https://github.com/django/django.git
Fixed #26528 -- Allowed any iterable (e.g. tuple) as validators kwarg for form/model fields.
This commit is contained in:
parent
ec6121693f
commit
a885bca1df
|
@ -5,6 +5,7 @@ import collections
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
|
import itertools
|
||||||
import uuid
|
import uuid
|
||||||
import warnings
|
import warnings
|
||||||
from base64 import b64decode, b64encode
|
from base64 import b64decode, b64encode
|
||||||
|
@ -531,9 +532,11 @@ class Field(RegisterLookupMixin):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def validators(self):
|
def validators(self):
|
||||||
# Some validators can't be created at field initialization time.
|
"""
|
||||||
# This method provides a way to delay their creation until required.
|
Some validators can't be created at field initialization time.
|
||||||
return self.default_validators + self._validators
|
This method provides a way to delay their creation until required.
|
||||||
|
"""
|
||||||
|
return list(itertools.chain(self.default_validators, self._validators))
|
||||||
|
|
||||||
def run_validators(self, value):
|
def run_validators(self, value):
|
||||||
if value in self.empty_values:
|
if value in self.empty_values:
|
||||||
|
|
|
@ -6,6 +6,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
|
import itertools
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
@ -119,7 +120,8 @@ class Field(object):
|
||||||
messages.update(error_messages or {})
|
messages.update(error_messages or {})
|
||||||
self.error_messages = messages
|
self.error_messages = messages
|
||||||
|
|
||||||
self.validators = self.default_validators + validators
|
self.validators = list(itertools.chain(self.default_validators, validators))
|
||||||
|
|
||||||
super(Field, self).__init__()
|
super(Field, self).__init__()
|
||||||
|
|
||||||
def prepare_value(self, value):
|
def prepare_value(self, value):
|
||||||
|
|
|
@ -52,3 +52,17 @@ class TestFieldWithValidators(TestCase):
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertEqual(form.errors['string'], ["Letters only."])
|
self.assertEqual(form.errors['string'], ["Letters only."])
|
||||||
self.assertEqual(form.errors['string'], ["Letters only."])
|
self.assertEqual(form.errors['string'], ["Letters only."])
|
||||||
|
|
||||||
|
def test_field_validators_can_be_any_iterable(self):
|
||||||
|
class UserForm(forms.Form):
|
||||||
|
full_name = forms.CharField(
|
||||||
|
max_length=50,
|
||||||
|
validators=(
|
||||||
|
validators.validate_integer,
|
||||||
|
validators.validate_email,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
form = UserForm({'full_name': 'not int nor mail'})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertEqual(form.errors['full_name'], ['Enter a valid integer.', 'Enter a valid email address.'])
|
||||||
|
|
|
@ -31,6 +31,8 @@ class ModelToValidate(models.Model):
|
||||||
)
|
)
|
||||||
url = models.URLField(blank=True)
|
url = models.URLField(blank=True)
|
||||||
f_with_custom_validator = models.IntegerField(blank=True, null=True, validators=[validate_answer_to_universe])
|
f_with_custom_validator = models.IntegerField(blank=True, null=True, validators=[validate_answer_to_universe])
|
||||||
|
f_with_iterable_of_validators = models.IntegerField(blank=True, null=True,
|
||||||
|
validators=(validate_answer_to_universe,))
|
||||||
slug = models.SlugField(blank=True)
|
slug = models.SlugField(blank=True)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
|
@ -6,14 +6,26 @@ from .models import ModelToValidate
|
||||||
|
|
||||||
class TestModelsWithValidators(ValidationTestCase):
|
class TestModelsWithValidators(ValidationTestCase):
|
||||||
def test_custom_validator_passes_for_correct_value(self):
|
def test_custom_validator_passes_for_correct_value(self):
|
||||||
mtv = ModelToValidate(number=10, name='Some Name', f_with_custom_validator=42)
|
mtv = ModelToValidate(number=10, name='Some Name', f_with_custom_validator=42,
|
||||||
|
f_with_iterable_of_validators=42)
|
||||||
self.assertIsNone(mtv.full_clean())
|
self.assertIsNone(mtv.full_clean())
|
||||||
|
|
||||||
def test_custom_validator_raises_error_for_incorrect_value(self):
|
def test_custom_validator_raises_error_for_incorrect_value(self):
|
||||||
mtv = ModelToValidate(number=10, name='Some Name', f_with_custom_validator=12)
|
mtv = ModelToValidate(number=10, name='Some Name', f_with_custom_validator=12,
|
||||||
|
f_with_iterable_of_validators=42)
|
||||||
self.assertFailsValidation(mtv.full_clean, ['f_with_custom_validator'])
|
self.assertFailsValidation(mtv.full_clean, ['f_with_custom_validator'])
|
||||||
self.assertFieldFailsValidationWithMessage(
|
self.assertFieldFailsValidationWithMessage(
|
||||||
mtv.full_clean,
|
mtv.full_clean,
|
||||||
'f_with_custom_validator',
|
'f_with_custom_validator',
|
||||||
['This is not the answer to life, universe and everything!']
|
['This is not the answer to life, universe and everything!']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_field_validators_can_be_any_iterable(self):
|
||||||
|
mtv = ModelToValidate(number=10, name='Some Name', f_with_custom_validator=42,
|
||||||
|
f_with_iterable_of_validators=12)
|
||||||
|
self.assertFailsValidation(mtv.full_clean, ['f_with_iterable_of_validators'])
|
||||||
|
self.assertFieldFailsValidationWithMessage(
|
||||||
|
mtv.full_clean,
|
||||||
|
'f_with_iterable_of_validators',
|
||||||
|
['This is not the answer to life, universe and everything!']
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue