diff --git a/django/forms/utils.py b/django/forms/utils.py index 44447b5cf5..4af57e8586 100644 --- a/django/forms/utils.py +++ b/django/forms/utils.py @@ -1,5 +1,5 @@ import json -from collections import UserDict, UserList +from collections import UserList from django.conf import settings from django.core.exceptions import ValidationError @@ -84,7 +84,7 @@ class RenderableErrorMixin(RenderableMixin): return self.render(self.template_name_ul) -class ErrorDict(UserDict, RenderableErrorMixin): +class ErrorDict(dict, RenderableErrorMixin): """ A collection of errors that knows how to display itself in various formats. @@ -94,8 +94,8 @@ class ErrorDict(UserDict, RenderableErrorMixin): template_name_text = 'django/forms/errors/dict/text.txt' template_name_ul = 'django/forms/errors/dict/ul.html' - def __init__(self, data=None, renderer=None): - super().__init__(data) + def __init__(self, *args, renderer=None, **kwargs): + super().__init__(*args, **kwargs) self.renderer = renderer or get_default_renderer() def as_data(self): diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py index 9af103dbd8..fefebde05c 100644 --- a/tests/forms_tests/tests/test_forms.py +++ b/tests/forms_tests/tests/test_forms.py @@ -67,6 +67,7 @@ class FormsTestCase(SimpleTestCase): self.assertTrue(p.is_bound) self.assertEqual(p.errors, {}) + self.assertIsInstance(p.errors, dict) self.assertTrue(p.is_valid()) self.assertHTMLEqual(p.errors.as_ul(), '') self.assertEqual(p.errors.as_text(), '') diff --git a/tests/forms_tests/tests/test_utils.py b/tests/forms_tests/tests/test_utils.py index ea0949920e..e0e9a0e22e 100644 --- a/tests/forms_tests/tests/test_utils.py +++ b/tests/forms_tests/tests/test_utils.py @@ -1,4 +1,5 @@ import copy +import json from django.core.exceptions import ValidationError from django.forms.utils import ErrorDict, ErrorList, flatatt @@ -162,3 +163,48 @@ class FormsUtilsTestCase(SimpleTestCase): e = ErrorList(['Invalid username.']) self.assertTrue(hasattr(ErrorList, '__html__')) self.assertEqual(str(e), e.__html__()) + + def test_error_dict_is_dict(self): + self.assertIsInstance(ErrorDict(), dict) + + def test_error_dict_is_json_serializable(self): + init_errors = ErrorDict([ + ('__all__', ErrorList([ + ValidationError('Sorry this form only works on leap days.') + ])), + ('name', ErrorList([ValidationError('This field is required.')])), + ]) + min_value_error_list = ErrorList([ + ValidationError('Ensure this value is greater than or equal to 0.') + ]) + e = ErrorDict( + init_errors, + date=ErrorList([ + ErrorDict({ + 'day': min_value_error_list, + 'month': min_value_error_list, + 'year': min_value_error_list, + }), + ]), + ) + e['renderer'] = ErrorList([ + ValidationError( + 'Select a valid choice. That choice is not one of the ' + 'available choices.' + ), + ]) + self.assertJSONEqual(json.dumps(e), { + '__all__': ['Sorry this form only works on leap days.'], + 'name': ['This field is required.'], + 'date': [ + { + 'day': ['Ensure this value is greater than or equal to 0.'], + 'month': ['Ensure this value is greater than or equal to 0.'], + 'year': ['Ensure this value is greater than or equal to 0.'], + }, + ], + 'renderer': [ + 'Select a valid choice. That choice is not one of the ' + 'available choices.' + ], + })