Commit 3e64f3d0 authored by Bertrand Bordage's avatar Bertrand Bordage Committed by Tim Graham
Browse files

Fixed #24531 -- Improved CommaSeparatedIntegerField validation.

`','`, `'1,,1'`, `',1'` etc. are no longer considered as valid
comma-separated integer lists.
parent f4cc0c40
Loading
Loading
Loading
Loading
+8 −5
Original line number Diff line number Diff line
@@ -254,11 +254,14 @@ def ip_address_validators(protocol, unpack_ipv4):
        raise ValueError("The protocol '%s' is unknown. Supported: %s"
                         % (protocol, list(ip_address_validator_map)))

comma_separated_int_list_re = re.compile('^[\d,]+$')
validate_comma_separated_integer_list = RegexValidator(
    comma_separated_int_list_re,
    _('Enter only digits separated by commas.'),
    'invalid'

def int_list_validator(sep=',', message=None, code='invalid'):
    regexp = re.compile('^\d+(?:%s\d+)*$' % re.escape(sep))
    return RegexValidator(regexp, message=message, code=code)


validate_comma_separated_integer_list = int_list_validator(
    message=_('Enter only digits separated by commas.'),
)


+10 −0
Original line number Diff line number Diff line
@@ -214,6 +214,16 @@ to, or in lieu of custom ``field.clean()`` methods.
    A :class:`RegexValidator` instance that ensures a value is a
    comma-separated list of integers.

``int_list_validator``
----------------------

.. function:: int_list_validator(sep=',', message=None, code='invalid')

    .. versionadded:: 1.9

    Returns a :class:`RegexValidator` instance that ensures a string
    consists of integers separated by ``sep``.

``MaxValueValidator``
---------------------

+5 −1
Original line number Diff line number Diff line
@@ -213,7 +213,8 @@ URLs
Validators
^^^^^^^^^^

* ...
* Added :func:`django.core.validators.int_list_validator` to generate
  validators of strings containing integers separated with a custom character.

Backwards incompatible changes in 1.9
=====================================
@@ -321,6 +322,9 @@ Miscellaneous
  that value to construct absolute URLs have been moved to CSS for easier
  customization.

* ``CommaSeparatedIntegerField`` validation has been refined to forbid values
  like ``','``, ``',1'``, and ``'1,,2'``.

.. _deprecated-features-1.9:

Features deprecated in 1.9
+11 −7
Original line number Diff line number Diff line
@@ -2136,24 +2136,28 @@ class ModelOtherFieldTests(TestCase):
                model = CommaSeparatedInteger
                fields = '__all__'

        f = CommaSeparatedIntegerForm({'field': '1'})
        self.assertTrue(f.is_valid())
        self.assertEqual(f.cleaned_data, {'field': '1'})
        f = CommaSeparatedIntegerForm({'field': '12'})
        self.assertTrue(f.is_valid())
        self.assertEqual(f.cleaned_data, {'field': '12'})
        f = CommaSeparatedIntegerForm({'field': '1,2,3'})
        self.assertTrue(f.is_valid())
        self.assertEqual(f.cleaned_data, {'field': '1,2,3'})
        f = CommaSeparatedIntegerForm({'field': '10,32'})
        self.assertTrue(f.is_valid())
        self.assertEqual(f.cleaned_data, {'field': '10,32'})
        f = CommaSeparatedIntegerForm({'field': '1a,2'})
        self.assertEqual(f.errors, {'field': ['Enter only digits separated by commas.']})
        f = CommaSeparatedIntegerForm({'field': ',,,,'})
        self.assertTrue(f.is_valid())
        self.assertEqual(f.cleaned_data, {'field': ',,,,'})
        self.assertEqual(f.errors, {'field': ['Enter only digits separated by commas.']})
        f = CommaSeparatedIntegerForm({'field': '1.2'})
        self.assertEqual(f.errors, {'field': ['Enter only digits separated by commas.']})
        f = CommaSeparatedIntegerForm({'field': '1,a,2'})
        self.assertEqual(f.errors, {'field': ['Enter only digits separated by commas.']})
        f = CommaSeparatedIntegerForm({'field': '1,,2'})
        self.assertTrue(f.is_valid())
        self.assertEqual(f.cleaned_data, {'field': '1,,2'})
        f = CommaSeparatedIntegerForm({'field': '1'})
        self.assertTrue(f.is_valid())
        self.assertEqual(f.cleaned_data, {'field': '1'})
        self.assertEqual(f.errors, {'field': ['Enter only digits separated by commas.']})

    def test_url_on_modelform(self):
        "Check basic URL field validation on model forms"
+15 −4
Original line number Diff line number Diff line
@@ -12,9 +12,9 @@ from django.core.exceptions import ValidationError
from django.core.validators import (
    BaseValidator, EmailValidator, MaxLengthValidator, MaxValueValidator,
    MinLengthValidator, MinValueValidator, RegexValidator, URLValidator,
    validate_comma_separated_integer_list, validate_email, validate_integer,
    validate_ipv4_address, validate_ipv6_address, validate_ipv46_address,
    validate_slug,
    int_list_validator, validate_comma_separated_integer_list, validate_email,
    validate_integer, validate_ipv4_address, validate_ipv6_address,
    validate_ipv46_address, validate_slug,
)
from django.test import SimpleTestCase
from django.test.utils import str_prefix
@@ -120,12 +120,23 @@ TEST_DATA = [
    (validate_ipv46_address, '12345::', ValidationError),

    (validate_comma_separated_integer_list, '1', None),
    (validate_comma_separated_integer_list, '12', None),
    (validate_comma_separated_integer_list, '1,2', None),
    (validate_comma_separated_integer_list, '1,2,3', None),
    (validate_comma_separated_integer_list, '1,2,3,', None),
    (validate_comma_separated_integer_list, '10,32', None),

    (validate_comma_separated_integer_list, '', ValidationError),
    (validate_comma_separated_integer_list, 'a', ValidationError),
    (validate_comma_separated_integer_list, 'a,b,c', ValidationError),
    (validate_comma_separated_integer_list, '1, 2, 3', ValidationError),
    (validate_comma_separated_integer_list, ',', ValidationError),
    (validate_comma_separated_integer_list, '1,2,3,', ValidationError),
    (validate_comma_separated_integer_list, '1,2,', ValidationError),
    (validate_comma_separated_integer_list, ',1', ValidationError),
    (validate_comma_separated_integer_list, '1,,2', ValidationError),

    (int_list_validator(sep='.'), '1.2.3', None),
    (int_list_validator(sep='.'), '1,2,3', ValidationError),

    (MaxValueValidator(10), 10, None),
    (MaxValueValidator(10), -10, None),