Fixed #26528 -- Allowed any iterable (e.g. tuple) as validators kwarg for form/model fields.

This commit is contained in:
Loïc Bistuer 2016-04-22 07:18:43 +07:00
parent ec6121693f
commit a885bca1df
5 changed files with 39 additions and 6 deletions

View File

@ -5,6 +5,7 @@ import collections
import copy
import datetime
import decimal
import itertools
import uuid
import warnings
from base64 import b64decode, b64encode
@ -531,9 +532,11 @@ class Field(RegisterLookupMixin):
@cached_property
def validators(self):
# Some validators can't be created at field initialization time.
# This method provides a way to delay their creation until required.
return self.default_validators + self._validators
"""
Some validators can't be created at field initialization time.
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):
if value in self.empty_values:

View File

@ -6,6 +6,7 @@ from __future__ import unicode_literals
import copy
import datetime
import itertools
import os
import re
import sys
@ -119,7 +120,8 @@ class Field(object):
messages.update(error_messages or {})
self.error_messages = messages
self.validators = self.default_validators + validators
self.validators = list(itertools.chain(self.default_validators, validators))
super(Field, self).__init__()
def prepare_value(self, value):

View File

@ -52,3 +52,17 @@ class TestFieldWithValidators(TestCase):
self.assertFalse(form.is_valid())
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.'])

View File

@ -31,6 +31,8 @@ class ModelToValidate(models.Model):
)
url = models.URLField(blank=True)
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)
def clean(self):

View File

@ -6,14 +6,26 @@ from .models import ModelToValidate
class TestModelsWithValidators(ValidationTestCase):
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())
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.assertFieldFailsValidationWithMessage(
mtv.full_clean,
'f_with_custom_validator',
['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!']
)