diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py index 318c393c09..86ab0f263e 100644 --- a/django/contrib/admin/helpers.py +++ b/django/contrib/admin/helpers.py @@ -329,6 +329,8 @@ class AdminErrorList(forms.utils.ErrorList): Stores all errors for the form/formsets in an add/change stage view. """ def __init__(self, form, inline_formsets): + super(AdminErrorList, self).__init__() + if form.is_bound: self.extend(list(six.itervalues(form.errors))) for inline_formset in inline_formsets: diff --git a/django/forms/fields.py b/django/forms/fields.py index 6f88fb2662..fe056e3271 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -15,7 +15,7 @@ from io import BytesIO from django.core import validators from django.core.exceptions import ValidationError -from django.forms.utils import ErrorList, from_current_timezone, to_current_timezone +from django.forms.utils import from_current_timezone, to_current_timezone from django.forms.widgets import ( TextInput, NumberInput, EmailInput, URLInput, HiddenInput, MultipleHiddenInput, ClearableFileInput, CheckboxInput, Select, @@ -998,7 +998,7 @@ class MultiValueField(Field): DateField.clean(value[0]) and TimeField.clean(value[1]). """ clean_data = [] - errors = ErrorList() + errors = [] if not value or isinstance(value, (list, tuple)): if not value or not [v for v in value if v not in self.empty_values]: if self.required: diff --git a/django/forms/forms.py b/django/forms/forms.py index 350b8b8993..880a74f49e 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -321,9 +321,9 @@ class BaseForm(object): "argument contains errors for multiple fields." ) else: - error = dict(error) + error = error.error_dict else: - error = {field or NON_FIELD_ERRORS: list(error)} + error = {field or NON_FIELD_ERRORS: error.error_list} for field, error_list in error.items(): if field not in self.errors: diff --git a/django/forms/formsets.py b/django/forms/formsets.py index fd1fd92773..215ec1d876 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -341,7 +341,7 @@ class BaseFormSet(object): # Give self.clean() a chance to do cross-form validation. self.clean() except ValidationError as e: - self._non_form_errors = self.error_class(e.messages) + self._non_form_errors = self.error_class(e.error_list) def clean(self): """ diff --git a/django/forms/utils.py b/django/forms/utils.py index 9402f2612e..3582384293 100644 --- a/django/forms/utils.py +++ b/django/forms/utils.py @@ -1,5 +1,7 @@ from __future__ import unicode_literals +import json +import sys import warnings from django.conf import settings @@ -8,12 +10,16 @@ from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from django.utils import six -import sys # Import ValidationError so that it can be imported from this # module to maintain backwards compatibility. from django.core.exceptions import ValidationError +try: + from collections import UserList +except ImportError: # Python 2 + from UserList import UserList + def flatatt(attrs): """ @@ -46,8 +52,12 @@ class ErrorDict(dict): The dictionary keys are the field names, and the values are the errors. """ - def __str__(self): - return self.as_ul() + def as_data(self): + return {f: e.as_data() for f, e in self.items()} + + def as_json(self): + errors = {f: json.loads(e.as_json()) for f, e in self.items()} + return json.dumps(errors) def as_ul(self): if not self: @@ -58,19 +68,35 @@ class ErrorDict(dict): ) def as_text(self): - return '\n'.join('* %s\n%s' % (k, '\n'.join(' * %s' % force_text(i) for i in v)) for k, v in self.items()) + output = [] + for field, errors in self.items(): + output.append('* %s' % field) + output.append('\n'.join(' * %s' % e for e in errors)) + return '\n'.join(output) - -@python_2_unicode_compatible -class ErrorList(list): - """ - A collection of errors that knows how to display itself in various formats. - """ def __str__(self): return self.as_ul() + +@python_2_unicode_compatible +class ErrorList(UserList): + """ + A collection of errors that knows how to display itself in various formats. + """ + def as_data(self): + return self.data + + def as_json(self): + errors = [] + for error in ValidationError(self.data).error_list: + errors.append({ + 'message': list(error)[0], + 'code': error.code or '', + }) + return json.dumps(errors) + def as_ul(self): - if not self: + if not self.data: return '' return format_html( '