Fixed #33307 -- Allowed Form.add_error() with ValidationError dict.

Form.add_error() raised a TypeError when called with a field name and a
ValidationError that has an error_dict with a single entry for the same
field. This was confusing because the error message referred to "multiple
fields" when only one field was involved.

Modified Form.add_error() to allow this specific use case. Also ensured
that unbound forms do not raise an AttributeError when add_error() is
called.
This commit is contained in:
google-labs-jules[bot] 2025-09-24 00:41:22 +00:00
parent 1acb00b26d
commit 8b2cd9183d
2 changed files with 34 additions and 2 deletions

View file

@ -285,7 +285,11 @@ class BaseForm(RenderableFormMixin):
error = ValidationError(error) error = ValidationError(error)
if hasattr(error, "error_dict"): if hasattr(error, "error_dict"):
if field is not None: if field is None:
error = error.error_dict
# Raise an error if the ValidationError contains more than one
# item or if the only item's key doesn't match the form field.
elif len(error.error_dict) != 1 or field not in error.error_dict:
raise TypeError( raise TypeError(
"The argument `field` must be `None` when the `error` " "The argument `field` must be `None` when the `error` "
"argument contains errors for multiple fields." "argument contains errors for multiple fields."
@ -312,7 +316,7 @@ class BaseForm(RenderableFormMixin):
field_id=self[field].auto_id, field_id=self[field].auto_id,
) )
self._errors[field].extend(error_list) self._errors[field].extend(error_list)
if field in self.cleaned_data: if hasattr(self, "cleaned_data") and field in self.cleaned_data:
del self.cleaned_data[field] del self.cleaned_data[field]
def has_error(self, field, code=None): def has_error(self, field, code=None):

View file

@ -1710,6 +1710,34 @@ aria-describedby="id_birthday_error">
}, },
) )
def test_add_error_validation_error_dict(self):
class FooForm(Form):
the_field = CharField(max_length=100)
exc = ValidationError({"the_field": "Something is wrong with the field."})
form = FooForm()
form.add_error("the_field", exc)
self.assertEqual(form.errors, {"the_field": ["Something is wrong with the field."]})
def test_add_error_validation_error_dict_multiple_fields(self):
class FooForm(Form):
the_field = CharField(max_length=100)
another_field = CharField(max_length=100)
exc = ValidationError(
{
"the_field": "Something is wrong with the field.",
"another_field": "Something is wrong with this other field.",
}
)
form = FooForm()
msg = (
"The argument `field` must be `None` when the `error` argument "
"contains errors for multiple fields."
)
with self.assertRaisesMessage(TypeError, msg):
form.add_error("the_field", exc)
def test_has_error(self): def test_has_error(self):
class UserRegistration(Form): class UserRegistration(Form):
username = CharField(max_length=10) username = CharField(max_length=10)