From 3ce9829b615336b0f3ac39b080c27fc8cf5af483 Mon Sep 17 00:00:00 2001 From: Loic Bistuer Date: Sat, 30 Nov 2013 02:38:13 +0700 Subject: Fixed #17413 -- Serialization of form errors along with all metadata. --- django/forms/fields.py | 4 +-- django/forms/forms.py | 4 +-- django/forms/formsets.py | 2 +- django/forms/utils.py | 66 +++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 59 insertions(+), 17 deletions(-) (limited to 'django/forms') 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) + + def __str__(self): + return self.as_ul() @python_2_unicode_compatible -class ErrorList(list): +class ErrorList(UserList): """ A collection of errors that knows how to display itself in various formats. """ - def __str__(self): - return self.as_ul() + 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( '', @@ -78,12 +104,28 @@ class ErrorList(list): ) def as_text(self): - if not self: - return '' - return '\n'.join('* %s' % force_text(e) for e in self) + return '\n'.join('* %s' % e for e in self) + + def __str__(self): + return self.as_ul() def __repr__(self): - return repr([force_text(e) for e in self]) + return repr(list(self)) + + def __contains__(self, item): + return item in list(self) + + def __eq__(self, other): + return list(self) == other + + def __ne__(self, other): + return list(self) != other + + def __getitem__(self, i): + error = self.data[i] + if isinstance(error, ValidationError): + return list(error)[0] + return force_text(error) # Utilities for time zone support in DateTimeField et al. -- cgit v1.3