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 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:

View File

@ -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):

View File

@ -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.'])

View File

@ -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):

View File

@ -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!']
)