Fixed #24531 -- Improved CommaSeparatedIntegerField validation.

`','`, `'1,,1'`, `',1'` etc. are no longer considered as valid
comma-separated integer lists.
This commit is contained in:
Bertrand Bordage 2015-02-14 15:20:43 +01:00 committed by Tim Graham
parent f4cc0c40a8
commit 3e64f3d0fc
5 changed files with 49 additions and 17 deletions

View File

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

View File

@ -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``
---------------------

View File

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

View File

@ -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"

View File

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