Commit 4d0c5f61 authored by Dejan Noveski's avatar Dejan Noveski Committed by Erik Romijn
Browse files

Fixed #22255 -- Added support for specifying re flags in RegexValidator

parent f2eea960
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -21,8 +21,9 @@ class RegexValidator(object):
    message = _('Enter a valid value.')
    code = 'invalid'
    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:
            self.regex = regex
        if message is not None:
@@ -31,10 +32,14 @@ class RegexValidator(object):
            self.code = code
        if inverse_match is not None:
            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.
        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):
        """
+13 −1
Original line number Diff line number Diff line
@@ -59,13 +59,16 @@ to, or in lieu of custom ``field.clean()`` methods.

``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
        expression string or a pre-compiled regular expression.
    :param message: If not ``None``, overrides :attr:`.message`.
    :param code: If not ``None``, overrides :attr:`code`.
    :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

@@ -93,6 +96,15 @@ to, or in lieu of custom ``field.clean()`` methods.

        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``
----------------
.. class:: URLValidator([schemes=None, regex=None, message=None, code=None])
+15 −4
Original line number Diff line number Diff line
@@ -795,11 +795,15 @@ Tests
Validators
^^^^^^^^^^

* :class:`~django.core.validators.RegexValidator` now accepts an optional
  Boolean :attr:`~django.core.validators.RegexValidator.inverse_match` argument
  which determines if the :exc:`~django.core.exceptions.ValidationError` should
* :class:`~django.core.validators.RegexValidator` now accepts the optional
  :attr:`~django.core.validators.RegexValidator.flags` and
  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
  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
  ``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,
  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:

Features deprecated in 1.7
+14 −1
Original line number Diff line number Diff line
from __future__ import unicode_literals

import re
from unittest import TestCase

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

        try:
@@ -38,3 +50,4 @@ class TestFieldWithValidators(TestCase):

        self.assertFalse(form.is_valid())
        self.assertEqual(form.errors['string'], ["Letters only."])
        self.assertEqual(form.errors['string'], ["Letters only."])
+12 −0
Original line number Diff line number Diff line
@@ -198,6 +198,10 @@ TEST_DATA = (
    (RegexValidator(re.compile('x'), inverse_match=True), 'y', None),
    (RegexValidator('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(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
for validator, value, expected in TEST_DATA:
    name, method = create_simple_test_method(validator, expected, value, test_counter)