diff --git a/django/forms/utils.py b/django/forms/utils.py
index e3151c9390..587cea85a3 100644
--- a/django/forms/utils.py
+++ b/django/forms/utils.py
@@ -51,8 +51,11 @@ class ErrorDict(dict):
def as_data(self):
return {f: e.as_data() for f, e in self.items()}
+ def get_json_data(self, escape_html=False):
+ return {f: e.get_json_data(escape_html) for f, e in self.items()}
+
def as_json(self, escape_html=False):
- return json.dumps({f: e.get_json_data(escape_html) for f, e in self.items()})
+ return json.dumps(self.get_json_data(escape_html))
def as_ul(self):
if not self:
diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt
index fda7472105..72ae044090 100644
--- a/docs/ref/forms/api.txt
+++ b/docs/ref/forms/api.txt
@@ -165,6 +165,17 @@ If for some reason you don't want to use client-side escaping, you can also
set ``escape_html=True`` and error messages will be escaped so you can use them
directly in HTML.
+.. method:: Form.errors.get_json_data(escape_html=False)
+
+.. versionadded:: 2.0
+
+Returns the errors as a dictionary suitable for serializing to JSON.
+:meth:`Form.errors.as_json()` returns serialized JSON, while this returns the
+error data before it's serialized.
+
+The ``escape_html`` parameter behaves as described in
+:meth:`Form.errors.as_json()`.
+
.. method:: Form.add_error(field, error)
This method allows adding errors to specific fields from within the
diff --git a/docs/releases/2.0.txt b/docs/releases/2.0.txt
index ee9ae181b4..e4b7110560 100644
--- a/docs/releases/2.0.txt
+++ b/docs/releases/2.0.txt
@@ -166,6 +166,10 @@ Forms
HTML attributes for the ``DateInput`` and ``TimeInput`` (or hidden)
subwidgets.
+* The new :meth:`Form.errors.get_json_data()
+ ` method returns form errors as
+ a dictionary suitable for including in a JSON response.
+
Generic Views
~~~~~~~~~~~~~
diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py
index 63209a1f62..cc73819091 100644
--- a/tests/forms_tests/tests/test_forms.py
+++ b/tests/forms_tests/tests/test_forms.py
@@ -3216,13 +3216,14 @@ Good luck picking a username that doesn't already exist.
for error in control:
self.assertInHTML(error, errors)
- errors = json.loads(form.errors.as_json())
+ errors = form.errors.get_json_data()
control = {
'foo': [{'code': 'required', 'message': 'This field is required.'}],
'bar': [{'code': 'required', 'message': 'This field is required.'}],
'__all__': [{'code': 'secret', 'message': 'Non-field error.'}]
}
self.assertEqual(errors, control)
+ self.assertEqual(json.dumps(errors), form.errors.as_json())
def test_error_dict_as_json_escape_html(self):
"""#21962 - adding html escape flag to ErrorDict"""
@@ -3247,8 +3248,13 @@ Good luck picking a username that doesn't already exist.
errors = json.loads(form.errors.as_json())
self.assertEqual(errors, control)
+ escaped_error = '<p>Non-field error.</p>'
+ self.assertEqual(
+ form.errors.get_json_data(escape_html=True)['__all__'][0]['message'],
+ escaped_error
+ )
errors = json.loads(form.errors.as_json(escape_html=True))
- control['__all__'][0]['message'] = '<p>Non-field error.</p>'
+ control['__all__'][0]['message'] = escaped_error
self.assertEqual(errors, control)
def test_error_list(self):
@@ -3270,10 +3276,12 @@ Good luck picking a username that doesn't already exist.
''
)
+ errors = e.get_json_data()
self.assertEqual(
- json.loads(e.as_json()),
+ errors,
[{"message": "Foo", "code": ""}, {"message": "Foobar", "code": "foobar"}]
)
+ self.assertEqual(json.dumps(errors), e.as_json())
def test_error_list_class_not_specified(self):
e = ErrorList()