diff --git a/django/core/validators.py b/django/core/validators.py index 6f4f570765..6bbee8e7d2 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -20,14 +20,17 @@ class RegexValidator(object): regex = '' message = _('Enter a valid value.') code = 'invalid' + inverse_match = False - def __init__(self, regex=None, message=None, code=None): + def __init__(self, regex=None, message=None, code=None, inverse_match=None): if regex is not None: self.regex = regex if message is not None: self.message = message if code is not None: self.code = code + if inverse_match is not None: + self.inverse_match = inverse_match # Compile the regex if it was not passed pre-compiled. if isinstance(self.regex, six.string_types): @@ -35,9 +38,11 @@ class RegexValidator(object): def __call__(self, value): """ - Validates that the input matches the regular expression. + Validates that the input matches the regular expression + if inverse_match is False, otherwise raises ValidationError. """ - if not self.regex.search(force_text(value)): + if not (self.inverse_match is not bool(self.regex.search( + force_text(value)))): raise ValidationError(self.message, code=self.code) def __eq__(self, other): diff --git a/docs/ref/validators.txt b/docs/ref/validators.txt index 1b706ab8e3..b3ae3c6d41 100644 --- a/docs/ref/validators.txt +++ b/docs/ref/validators.txt @@ -59,20 +59,22 @@ to, or in lieu of custom ``field.clean()`` methods. ``RegexValidator`` ------------------ -.. class:: RegexValidator([regex=None, message=None, code=None]) +.. class:: RegexValidator([regex=None, message=None, code=None, inverse_match=None]) :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`. .. attribute:: regex The regular expression pattern to search for the provided ``value``, or a pre-compiled regular expression. Raises a :exc:`~django.core.exceptions.ValidationError` with :attr:`message` - and :attr:`code` if no match is found. By default, matches any string - (including an empty string). + and :attr:`code` if :attr:`inverse_match` is ``False`` and a match is + found, or if :attr:`inverse_match` is ``True`` and a match is not found. + By default, matches any string (including an empty string). .. attribute:: message @@ -85,6 +87,12 @@ to, or in lieu of custom ``field.clean()`` methods. The error code used by :exc:`~django.core.exceptions.ValidationError` if validation fails. Defaults to ``"invalid"``. + .. attribute:: inverse_match + + .. versionadded:: 1.7 + + The match mode for :attr:`regex`. Defaults to ``False``. + ``URLValidator`` ---------------- .. class:: URLValidator([schemes=None, regex=None, message=None, code=None]) diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index 813e425f05..255cc35fa3 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -697,6 +697,12 @@ 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 + be raised when the regular expression pattern matches (``True``) or does not + match (``False``, by default) the provided ``value``. + * :class:`~django.core.validators.URLValidator` now accepts an optional ``schemes`` argument which allows customization of the accepted URI schemes (instead of the defaults ``http(s)`` and ``ftp(s)``). diff --git a/tests/validators/tests.py b/tests/validators/tests.py index ec91486658..54e5dfbeed 100644 --- a/tests/validators/tests.py +++ b/tests/validators/tests.py @@ -187,6 +187,10 @@ TEST_DATA = ( (RegexValidator('x'), 'y', ValidationError), (RegexValidator(re.compile('x')), 'y', ValidationError), + (RegexValidator('x', inverse_match=True), 'y', None), + (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), )