Commit 7ac83807 authored by Loic Bistuer's avatar Loic Bistuer Committed by Tim Graham
Browse files

Fixed #22318 -- Added Form.has_error() to easily check if a given error has happened.

parent 3f7615cd
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -334,6 +334,15 @@ class BaseForm(object):
            if field in self.cleaned_data:
                del self.cleaned_data[field]

    def has_error(self, field, code=None):
        if code is None:
            return field in self.errors
        if field in self.errors:
            for error in self.errors.as_data()[field]:
                if error.code == code:
                    return True
        return False

    def full_clean(self):
        """
        Cleans all of self.data and populates self._errors and
+11 −0
Original line number Diff line number Diff line
@@ -182,6 +182,17 @@ when defining form errors.
Note that ``Form.add_error()`` automatically removes the relevant field from
``cleaned_data``.

.. method:: Form.has_error(field, code=None)

.. versionadded:: 1.8

This method returns a boolean designating whether a field has an error with
a specific error ``code``. If ``code`` is ``None``, it will return ``True``
if the field contains any errors at all.

To check for non-field errors use
:data:`~django.core.exceptions.NON_FIELD_ERRORS` as the ``field`` parameter.

Behavior of unbound forms
~~~~~~~~~~~~~~~~~~~~~~~~~

+3 −0
Original line number Diff line number Diff line
@@ -109,6 +109,9 @@ Forms
* Form widgets now render attributes with a value of ``True`` or ``False``
  as HTML5 boolean attributes.

* The new :meth:`~django.forms.Form.has_error()` method allows checking
  if a specific error has happened.

Internationalization
^^^^^^^^^^^^^^^^^^^^

+34 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ import datetime
import json
import warnings

from django.core.exceptions import NON_FIELD_ERRORS
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.validators import RegexValidator
from django.forms import (
@@ -739,6 +740,39 @@ class FormsTestCase(TestCase):
        with six.assertRaisesRegex(self, ValueError, "has no field named"):
            f.add_error('missing_field', 'Some error.')

    def test_has_error(self):
        class UserRegistration(Form):
            username = CharField(max_length=10)
            password1 = CharField(widget=PasswordInput, min_length=5)
            password2 = CharField(widget=PasswordInput)

            def clean(self):
                if (self.cleaned_data.get('password1') and self.cleaned_data.get('password2')
                        and self.cleaned_data['password1'] != self.cleaned_data['password2']):
                    raise ValidationError(
                        'Please make sure your passwords match.',
                        code='password_mismatch',
                    )

        f = UserRegistration(data={})
        self.assertTrue(f.has_error('password1'))
        self.assertTrue(f.has_error('password1', 'required'))
        self.assertFalse(f.has_error('password1', 'anything'))

        f = UserRegistration(data={'password1': 'Hi', 'password2': 'Hi'})
        self.assertTrue(f.has_error('password1'))
        self.assertTrue(f.has_error('password1', 'min_length'))
        self.assertFalse(f.has_error('password1', 'anything'))
        self.assertFalse(f.has_error('password2'))
        self.assertFalse(f.has_error('password2', 'anything'))

        f = UserRegistration(data={'password1': 'Bonjour', 'password2': 'Hello'})
        self.assertFalse(f.has_error('password1'))
        self.assertFalse(f.has_error('password1', 'required'))
        self.assertTrue(f.has_error(NON_FIELD_ERRORS))
        self.assertTrue(f.has_error(NON_FIELD_ERRORS, 'password_mismatch'))
        self.assertFalse(f.has_error(NON_FIELD_ERRORS, 'anything'))

    def test_dynamic_construction(self):
        # It's possible to construct a Form dynamically by adding to the self.fields
        # dictionary in __init__(). Don't forget to call Form.__init__() within the