Commit a885bca1 authored by Loïc Bistuer's avatar Loïc Bistuer
Browse files

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

parent ec612169
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -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:
+3 −1
Original line number Diff line number Diff line
@@ -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):
+14 −0
Original line number Diff line number Diff line
@@ -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.'])
+2 −0
Original line number Diff line number Diff line
@@ -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):
+14 −2
Original line number Diff line number Diff line
@@ -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!']
        )