Fixed #22255 -- Added support for specifying re flags in RegexValidator
This commit is contained in:
parent
f2eea960e0
commit
4d0c5f6142
|
@ -21,8 +21,9 @@ class RegexValidator(object):
|
||||||
message = _('Enter a valid value.')
|
message = _('Enter a valid value.')
|
||||||
code = 'invalid'
|
code = 'invalid'
|
||||||
inverse_match = False
|
inverse_match = False
|
||||||
|
flags = 0
|
||||||
|
|
||||||
def __init__(self, regex=None, message=None, code=None, inverse_match=None):
|
def __init__(self, regex=None, message=None, code=None, inverse_match=None, flags=None):
|
||||||
if regex is not None:
|
if regex is not None:
|
||||||
self.regex = regex
|
self.regex = regex
|
||||||
if message is not None:
|
if message is not None:
|
||||||
|
@ -31,10 +32,14 @@ class RegexValidator(object):
|
||||||
self.code = code
|
self.code = code
|
||||||
if inverse_match is not None:
|
if inverse_match is not None:
|
||||||
self.inverse_match = inverse_match
|
self.inverse_match = inverse_match
|
||||||
|
if flags is not None:
|
||||||
|
self.flags = flags
|
||||||
|
if self.flags and not isinstance(self.regex, six.string_types):
|
||||||
|
raise TypeError("If the flags are set, regex must be a regular expression string.")
|
||||||
|
|
||||||
# Compile the regex if it was not passed pre-compiled.
|
# Compile the regex if it was not passed pre-compiled.
|
||||||
if isinstance(self.regex, six.string_types):
|
if isinstance(self.regex, six.string_types):
|
||||||
self.regex = re.compile(self.regex)
|
self.regex = re.compile(self.regex, self.flags)
|
||||||
|
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -59,13 +59,16 @@ to, or in lieu of custom ``field.clean()`` methods.
|
||||||
|
|
||||||
``RegexValidator``
|
``RegexValidator``
|
||||||
------------------
|
------------------
|
||||||
.. class:: RegexValidator([regex=None, message=None, code=None, inverse_match=None])
|
.. class:: RegexValidator([regex=None, message=None, code=None, inverse_match=None, flags=0])
|
||||||
|
|
||||||
:param regex: If not ``None``, overrides :attr:`regex`. Can be a regular
|
:param regex: If not ``None``, overrides :attr:`regex`. Can be a regular
|
||||||
expression string or a pre-compiled regular expression.
|
expression string or a pre-compiled regular expression.
|
||||||
:param message: If not ``None``, overrides :attr:`.message`.
|
:param message: If not ``None``, overrides :attr:`.message`.
|
||||||
:param code: If not ``None``, overrides :attr:`code`.
|
:param code: If not ``None``, overrides :attr:`code`.
|
||||||
:param inverse_match: If not ``None``, overrides :attr:`inverse_match`.
|
:param inverse_match: If not ``None``, overrides :attr:`inverse_match`.
|
||||||
|
:param flags: If not ``None``, overrides :attr:`flags`. In that case,
|
||||||
|
:attr:`regex` must be a regular expression string, or
|
||||||
|
:exc:`~exceptions.TypeError` is raised.
|
||||||
|
|
||||||
.. attribute:: regex
|
.. attribute:: regex
|
||||||
|
|
||||||
|
@ -93,6 +96,15 @@ to, or in lieu of custom ``field.clean()`` methods.
|
||||||
|
|
||||||
The match mode for :attr:`regex`. Defaults to ``False``.
|
The match mode for :attr:`regex`. Defaults to ``False``.
|
||||||
|
|
||||||
|
.. attribute:: flags
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
The flags used when compiling the regular expression string :attr:`regex`.
|
||||||
|
If :attr:`regex` is a pre-compiled regular expression, and :attr:`flags` is overridden,
|
||||||
|
:exc:`~exceptions.TypeError` is raised.
|
||||||
|
Defaults to `0`.
|
||||||
|
|
||||||
``URLValidator``
|
``URLValidator``
|
||||||
----------------
|
----------------
|
||||||
.. class:: URLValidator([schemes=None, regex=None, message=None, code=None])
|
.. class:: URLValidator([schemes=None, regex=None, message=None, code=None])
|
||||||
|
|
|
@ -795,11 +795,15 @@ Tests
|
||||||
Validators
|
Validators
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|
|
||||||
* :class:`~django.core.validators.RegexValidator` now accepts an optional
|
* :class:`~django.core.validators.RegexValidator` now accepts the optional
|
||||||
Boolean :attr:`~django.core.validators.RegexValidator.inverse_match` argument
|
:attr:`~django.core.validators.RegexValidator.flags` and
|
||||||
which determines if the :exc:`~django.core.exceptions.ValidationError` should
|
Boolean :attr:`~django.core.validators.RegexValidator.inverse_match` arguments.
|
||||||
|
The :attr:`~django.core.validators.RegexValidator.inverse_match` attribute
|
||||||
|
determines if the :exc:`~django.core.exceptions.ValidationError` should
|
||||||
be raised when the regular expression pattern matches (``True``) or does not
|
be raised when the regular expression pattern matches (``True``) or does not
|
||||||
match (``False``, by default) the provided ``value``.
|
match (``False``, by default) the provided ``value``. The
|
||||||
|
:attr:`~django.core.validators.RegexValidator.flags` attribute sets the flags
|
||||||
|
used when compiling a regular expression string.
|
||||||
|
|
||||||
* :class:`~django.core.validators.URLValidator` now accepts an optional
|
* :class:`~django.core.validators.URLValidator` now accepts an optional
|
||||||
``schemes`` argument which allows customization of the accepted URI schemes
|
``schemes`` argument which allows customization of the accepted URI schemes
|
||||||
|
@ -1191,6 +1195,13 @@ Miscellaneous
|
||||||
a relation from the related object back to the content type for filtering,
|
a relation from the related object back to the content type for filtering,
|
||||||
ordering and other query operations.
|
ordering and other query operations.
|
||||||
|
|
||||||
|
* When a model field's :attr:`~django.db.models.Field.validators` contains
|
||||||
|
a :class:`~django.core.validators.RegexValidator`, the regular expression
|
||||||
|
must now be passed as a regular expression string. You can no longer use a
|
||||||
|
pre-compiled regular expression in this case, as it is not serializable.
|
||||||
|
The :attr:`~django.core.validators.RegexValidator.flags` attribute was added
|
||||||
|
to :class:`~django.core.validators.RegexValidator` to simplify this change.
|
||||||
|
|
||||||
.. _deprecated-features-1.7:
|
.. _deprecated-features-1.7:
|
||||||
|
|
||||||
Features deprecated in 1.7
|
Features deprecated in 1.7
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
@ -24,11 +25,22 @@ class UserForm(forms.Form):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
ignore_case_string = forms.CharField(
|
||||||
|
max_length=50,
|
||||||
|
validators=[
|
||||||
|
validators.RegexValidator(
|
||||||
|
regex='^[a-z]*$',
|
||||||
|
message="Letters only.",
|
||||||
|
flags=re.IGNORECASE,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestFieldWithValidators(TestCase):
|
class TestFieldWithValidators(TestCase):
|
||||||
def test_all_errors_get_reported(self):
|
def test_all_errors_get_reported(self):
|
||||||
form = UserForm({'full_name': 'not int nor mail', 'string': '2 is not correct'})
|
form = UserForm({'full_name': 'not int nor mail', 'string': '2 is not correct', 'ignore_case_string': "IgnORE Case strIng"})
|
||||||
self.assertRaises(ValidationError, form.fields['full_name'].clean, 'not int nor mail')
|
self.assertRaises(ValidationError, form.fields['full_name'].clean, 'not int nor mail')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -38,3 +50,4 @@ 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."])
|
||||||
|
|
|
@ -198,6 +198,10 @@ TEST_DATA = (
|
||||||
(RegexValidator(re.compile('x'), inverse_match=True), 'y', None),
|
(RegexValidator(re.compile('x'), inverse_match=True), 'y', None),
|
||||||
(RegexValidator('x', inverse_match=True), 'x', ValidationError),
|
(RegexValidator('x', inverse_match=True), 'x', ValidationError),
|
||||||
(RegexValidator(re.compile('x'), inverse_match=True), 'x', ValidationError),
|
(RegexValidator(re.compile('x'), inverse_match=True), 'x', ValidationError),
|
||||||
|
|
||||||
|
(RegexValidator('x', flags=re.IGNORECASE), 'y', ValidationError),
|
||||||
|
(RegexValidator('a'), 'A', ValidationError),
|
||||||
|
(RegexValidator('a', flags=re.IGNORECASE), 'A', None),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -250,6 +254,14 @@ class TestSimpleValidators(TestCase):
|
||||||
self.assertEqual(str(v), str_prefix("{%(_)s'first': [%(_)s'First Problem']}"))
|
self.assertEqual(str(v), str_prefix("{%(_)s'first': [%(_)s'First Problem']}"))
|
||||||
self.assertEqual(repr(v), str_prefix("ValidationError({%(_)s'first': [%(_)s'First Problem']})"))
|
self.assertEqual(repr(v), str_prefix("ValidationError({%(_)s'first': [%(_)s'First Problem']})"))
|
||||||
|
|
||||||
|
def test_regex_validator_flags(self):
|
||||||
|
try:
|
||||||
|
RegexValidator(re.compile('a'), flags=re.IGNORECASE)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.fail("TypeError not raised when flags and pre-compiled regex in RegexValidator")
|
||||||
|
|
||||||
test_counter = 0
|
test_counter = 0
|
||||||
for validator, value, expected in TEST_DATA:
|
for validator, value, expected in TEST_DATA:
|
||||||
name, method = create_simple_test_method(validator, expected, value, test_counter)
|
name, method = create_simple_test_method(validator, expected, value, test_counter)
|
||||||
|
|
Loading…
Reference in New Issue