Fixed #20199 -- Allow ModelForm fields to override error_messages from model fields
This commit is contained in:
parent
f34cfec0fa
commit
ee77d4b253
|
@ -22,15 +22,12 @@ class AdminAuthenticationForm(AuthenticationForm):
|
||||||
username = self.cleaned_data.get('username')
|
username = self.cleaned_data.get('username')
|
||||||
password = self.cleaned_data.get('password')
|
password = self.cleaned_data.get('password')
|
||||||
message = ERROR_MESSAGE
|
message = ERROR_MESSAGE
|
||||||
|
params = {'username': self.username_field.verbose_name}
|
||||||
|
|
||||||
if username and password:
|
if username and password:
|
||||||
self.user_cache = authenticate(username=username, password=password)
|
self.user_cache = authenticate(username=username, password=password)
|
||||||
if self.user_cache is None:
|
if self.user_cache is None:
|
||||||
raise forms.ValidationError(message % {
|
raise forms.ValidationError(message, code='invalid', params=params)
|
||||||
'username': self.username_field.verbose_name
|
|
||||||
})
|
|
||||||
elif not self.user_cache.is_active or not self.user_cache.is_staff:
|
elif not self.user_cache.is_active or not self.user_cache.is_staff:
|
||||||
raise forms.ValidationError(message % {
|
raise forms.ValidationError(message, code='invalid', params=params)
|
||||||
'username': self.username_field.verbose_name
|
|
||||||
})
|
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
|
@ -1574,13 +1574,13 @@ class InlineModelAdmin(BaseModelAdmin):
|
||||||
'class_name': p._meta.verbose_name,
|
'class_name': p._meta.verbose_name,
|
||||||
'instance': p}
|
'instance': p}
|
||||||
)
|
)
|
||||||
msg_dict = {'class_name': self._meta.model._meta.verbose_name,
|
params = {'class_name': self._meta.model._meta.verbose_name,
|
||||||
'instance': self.instance,
|
'instance': self.instance,
|
||||||
'related_objects': get_text_list(objs, _('and'))}
|
'related_objects': get_text_list(objs, _('and'))}
|
||||||
msg = _("Deleting %(class_name)s %(instance)s would require "
|
msg = _("Deleting %(class_name)s %(instance)s would require "
|
||||||
"deleting the following protected related objects: "
|
"deleting the following protected related objects: "
|
||||||
"%(related_objects)s") % msg_dict
|
"%(related_objects)s")
|
||||||
raise ValidationError(msg)
|
raise ValidationError(msg, code='deleting_protected', params=params)
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
result = super(DeleteProtectedModelForm, self).is_valid()
|
result = super(DeleteProtectedModelForm, self).is_valid()
|
||||||
|
|
|
@ -97,14 +97,19 @@ class UserCreationForm(forms.ModelForm):
|
||||||
User._default_manager.get(username=username)
|
User._default_manager.get(username=username)
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
return username
|
return username
|
||||||
raise forms.ValidationError(self.error_messages['duplicate_username'])
|
raise forms.ValidationError(
|
||||||
|
self.error_messages['duplicate_username'],
|
||||||
|
code='duplicate_username',
|
||||||
|
)
|
||||||
|
|
||||||
def clean_password2(self):
|
def clean_password2(self):
|
||||||
password1 = self.cleaned_data.get("password1")
|
password1 = self.cleaned_data.get("password1")
|
||||||
password2 = self.cleaned_data.get("password2")
|
password2 = self.cleaned_data.get("password2")
|
||||||
if password1 and password2 and password1 != password2:
|
if password1 and password2 and password1 != password2:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
self.error_messages['password_mismatch'])
|
self.error_messages['password_mismatch'],
|
||||||
|
code='password_mismatch',
|
||||||
|
)
|
||||||
return password2
|
return password2
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
|
@ -183,11 +188,15 @@ class AuthenticationForm(forms.Form):
|
||||||
password=password)
|
password=password)
|
||||||
if self.user_cache is None:
|
if self.user_cache is None:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
self.error_messages['invalid_login'] % {
|
self.error_messages['invalid_login'],
|
||||||
'username': self.username_field.verbose_name
|
code='invalid_login',
|
||||||
})
|
params={'username': self.username_field.verbose_name},
|
||||||
|
)
|
||||||
elif not self.user_cache.is_active:
|
elif not self.user_cache.is_active:
|
||||||
raise forms.ValidationError(self.error_messages['inactive'])
|
raise forms.ValidationError(
|
||||||
|
self.error_messages['inactive'],
|
||||||
|
code='inactive',
|
||||||
|
)
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
def check_for_test_cookie(self):
|
def check_for_test_cookie(self):
|
||||||
|
@ -269,7 +278,9 @@ class SetPasswordForm(forms.Form):
|
||||||
if password1 and password2:
|
if password1 and password2:
|
||||||
if password1 != password2:
|
if password1 != password2:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
self.error_messages['password_mismatch'])
|
self.error_messages['password_mismatch'],
|
||||||
|
code='password_mismatch',
|
||||||
|
)
|
||||||
return password2
|
return password2
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
|
@ -298,7 +309,9 @@ class PasswordChangeForm(SetPasswordForm):
|
||||||
old_password = self.cleaned_data["old_password"]
|
old_password = self.cleaned_data["old_password"]
|
||||||
if not self.user.check_password(old_password):
|
if not self.user.check_password(old_password):
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
self.error_messages['password_incorrect'])
|
self.error_messages['password_incorrect'],
|
||||||
|
code='password_incorrect',
|
||||||
|
)
|
||||||
return old_password
|
return old_password
|
||||||
|
|
||||||
PasswordChangeForm.base_fields = SortedDict([
|
PasswordChangeForm.base_fields = SortedDict([
|
||||||
|
@ -329,7 +342,9 @@ class AdminPasswordChangeForm(forms.Form):
|
||||||
if password1 and password2:
|
if password1 and password2:
|
||||||
if password1 != password2:
|
if password1 != password2:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
self.error_messages['password_mismatch'])
|
self.error_messages['password_mismatch'],
|
||||||
|
code='password_mismatch',
|
||||||
|
)
|
||||||
return password2
|
return password2
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
|
|
|
@ -17,11 +17,17 @@ class FlatpageForm(forms.ModelForm):
|
||||||
def clean_url(self):
|
def clean_url(self):
|
||||||
url = self.cleaned_data['url']
|
url = self.cleaned_data['url']
|
||||||
if not url.startswith('/'):
|
if not url.startswith('/'):
|
||||||
raise forms.ValidationError(ugettext("URL is missing a leading slash."))
|
raise forms.ValidationError(
|
||||||
|
ugettext("URL is missing a leading slash."),
|
||||||
|
code='missing_leading_slash',
|
||||||
|
)
|
||||||
if (settings.APPEND_SLASH and
|
if (settings.APPEND_SLASH and
|
||||||
'django.middleware.common.CommonMiddleware' in settings.MIDDLEWARE_CLASSES and
|
'django.middleware.common.CommonMiddleware' in settings.MIDDLEWARE_CLASSES and
|
||||||
not url.endswith('/')):
|
not url.endswith('/')):
|
||||||
raise forms.ValidationError(ugettext("URL is missing a trailing slash."))
|
raise forms.ValidationError(
|
||||||
|
ugettext("URL is missing a trailing slash."),
|
||||||
|
code='missing_trailing_slash',
|
||||||
|
)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
@ -36,7 +42,9 @@ class FlatpageForm(forms.ModelForm):
|
||||||
for site in sites:
|
for site in sites:
|
||||||
if same_url.filter(sites=site).exists():
|
if same_url.filter(sites=site).exists():
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
_('Flatpage with url %(url)s already exists for site %(site)s') %
|
_('Flatpage with url %(url)s already exists for site %(site)s'),
|
||||||
{'url': url, 'site': site})
|
code='duplicate_url',
|
||||||
|
params={'url': url, 'site': site},
|
||||||
|
)
|
||||||
|
|
||||||
return super(FlatpageForm, self).clean()
|
return super(FlatpageForm, self).clean()
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.forms import formsets, ValidationError
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.decorators import classonlymethod
|
from django.utils.decorators import classonlymethod
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
from django.contrib.formtools.wizard.storage import get_storage
|
from django.contrib.formtools.wizard.storage import get_storage
|
||||||
|
@ -271,7 +272,9 @@ class WizardView(TemplateView):
|
||||||
management_form = ManagementForm(self.request.POST, prefix=self.prefix)
|
management_form = ManagementForm(self.request.POST, prefix=self.prefix)
|
||||||
if not management_form.is_valid():
|
if not management_form.is_valid():
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
'ManagementForm data is missing or has been tampered.')
|
_('ManagementForm data is missing or has been tampered.'),
|
||||||
|
code='missing_management_form',
|
||||||
|
)
|
||||||
|
|
||||||
form_current_step = management_form.cleaned_data['current_step']
|
form_current_step = management_form.cleaned_data['current_step']
|
||||||
if (form_current_step != self.steps.current and
|
if (form_current_step != self.steps.current and
|
||||||
|
|
|
@ -50,7 +50,7 @@ class GeometryField(forms.Field):
|
||||||
try:
|
try:
|
||||||
return GEOSGeometry(value)
|
return GEOSGeometry(value)
|
||||||
except (GEOSException, ValueError, TypeError):
|
except (GEOSException, ValueError, TypeError):
|
||||||
raise forms.ValidationError(self.error_messages['invalid_geom'])
|
raise forms.ValidationError(self.error_messages['invalid_geom'], code='invalid_geom')
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
|
@ -65,7 +65,7 @@ class GeometryField(forms.Field):
|
||||||
# Ensuring that the geometry is of the correct type (indicated
|
# Ensuring that the geometry is of the correct type (indicated
|
||||||
# using the OGC string label).
|
# using the OGC string label).
|
||||||
if str(geom.geom_type).upper() != self.geom_type and not self.geom_type == 'GEOMETRY':
|
if str(geom.geom_type).upper() != self.geom_type and not self.geom_type == 'GEOMETRY':
|
||||||
raise forms.ValidationError(self.error_messages['invalid_geom_type'])
|
raise forms.ValidationError(self.error_messages['invalid_geom_type'], code='invalid_geom_type')
|
||||||
|
|
||||||
# Transforming the geometry if the SRID was set.
|
# Transforming the geometry if the SRID was set.
|
||||||
if self.srid:
|
if self.srid:
|
||||||
|
@ -76,7 +76,7 @@ class GeometryField(forms.Field):
|
||||||
try:
|
try:
|
||||||
geom.transform(self.srid)
|
geom.transform(self.srid)
|
||||||
except:
|
except:
|
||||||
raise forms.ValidationError(self.error_messages['transform_error'])
|
raise forms.ValidationError(self.error_messages['transform_error'], code='transform_error')
|
||||||
|
|
||||||
return geom
|
return geom
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,9 @@ def _simple_domain_name_validator(value):
|
||||||
checks = ((s in value) for s in string.whitespace)
|
checks = ((s in value) for s in string.whitespace)
|
||||||
if any(checks):
|
if any(checks):
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("The domain name cannot contain any spaces or tabs."))
|
_("The domain name cannot contain any spaces or tabs."),
|
||||||
|
code='invalid',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SiteManager(models.Manager):
|
class SiteManager(models.Manager):
|
||||||
|
|
|
@ -117,9 +117,7 @@ class ValidationError(Exception):
|
||||||
message = message.message
|
message = message.message
|
||||||
if params:
|
if params:
|
||||||
message %= params
|
message %= params
|
||||||
message = force_text(message)
|
message = force_text(message)
|
||||||
else:
|
|
||||||
message = force_text(message)
|
|
||||||
messages.append(message)
|
messages.append(message)
|
||||||
return messages
|
return messages
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ def validate_integer(value):
|
||||||
try:
|
try:
|
||||||
int(value)
|
int(value)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
raise ValidationError('')
|
raise ValidationError(_('Enter a valid integer.'), code='invalid')
|
||||||
|
|
||||||
|
|
||||||
class EmailValidator(object):
|
class EmailValidator(object):
|
||||||
|
@ -188,11 +188,7 @@ class BaseValidator(object):
|
||||||
cleaned = self.clean(value)
|
cleaned = self.clean(value)
|
||||||
params = {'limit_value': self.limit_value, 'show_value': cleaned}
|
params = {'limit_value': self.limit_value, 'show_value': cleaned}
|
||||||
if self.compare(cleaned, self.limit_value):
|
if self.compare(cleaned, self.limit_value):
|
||||||
raise ValidationError(
|
raise ValidationError(self.message, code=self.code, params=params)
|
||||||
self.message % params,
|
|
||||||
code=self.code,
|
|
||||||
params=params,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class MaxValueValidator(BaseValidator):
|
class MaxValueValidator(BaseValidator):
|
||||||
|
|
|
@ -77,7 +77,7 @@ class Field(object):
|
||||||
auto_creation_counter = -1
|
auto_creation_counter = -1
|
||||||
default_validators = [] # Default set of validators
|
default_validators = [] # Default set of validators
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid_choice': _('Value %r is not a valid choice.'),
|
'invalid_choice': _('Value %(value)r is not a valid choice.'),
|
||||||
'null': _('This field cannot be null.'),
|
'null': _('This field cannot be null.'),
|
||||||
'blank': _('This field cannot be blank.'),
|
'blank': _('This field cannot be blank.'),
|
||||||
'unique': _('%(model_name)s with this %(field_label)s '
|
'unique': _('%(model_name)s with this %(field_label)s '
|
||||||
|
@ -233,14 +233,17 @@ class Field(object):
|
||||||
return
|
return
|
||||||
elif value == option_key:
|
elif value == option_key:
|
||||||
return
|
return
|
||||||
msg = self.error_messages['invalid_choice'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid_choice'],
|
||||||
|
code='invalid_choice',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
if value is None and not self.null:
|
if value is None and not self.null:
|
||||||
raise exceptions.ValidationError(self.error_messages['null'])
|
raise exceptions.ValidationError(self.error_messages['null'], code='null')
|
||||||
|
|
||||||
if not self.blank and value in self.empty_values:
|
if not self.blank and value in self.empty_values:
|
||||||
raise exceptions.ValidationError(self.error_messages['blank'])
|
raise exceptions.ValidationError(self.error_messages['blank'], code='blank')
|
||||||
|
|
||||||
def clean(self, value, model_instance):
|
def clean(self, value, model_instance):
|
||||||
"""
|
"""
|
||||||
|
@ -568,7 +571,7 @@ class AutoField(Field):
|
||||||
|
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _("'%s' value must be an integer."),
|
'invalid': _("'%(value)s' value must be an integer."),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -586,8 +589,11 @@ class AutoField(Field):
|
||||||
try:
|
try:
|
||||||
return int(value)
|
return int(value)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
msg = self.error_messages['invalid'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid'],
|
||||||
|
code='invalid',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
def validate(self, value, model_instance):
|
def validate(self, value, model_instance):
|
||||||
pass
|
pass
|
||||||
|
@ -616,7 +622,7 @@ class AutoField(Field):
|
||||||
class BooleanField(Field):
|
class BooleanField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _("'%s' value must be either True or False."),
|
'invalid': _("'%(value)s' value must be either True or False."),
|
||||||
}
|
}
|
||||||
description = _("Boolean (Either True or False)")
|
description = _("Boolean (Either True or False)")
|
||||||
|
|
||||||
|
@ -636,8 +642,11 @@ class BooleanField(Field):
|
||||||
return True
|
return True
|
||||||
if value in ('f', 'False', '0'):
|
if value in ('f', 'False', '0'):
|
||||||
return False
|
return False
|
||||||
msg = self.error_messages['invalid'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid'],
|
||||||
|
code='invalid',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
def get_prep_lookup(self, lookup_type, value):
|
def get_prep_lookup(self, lookup_type, value):
|
||||||
# Special-case handling for filters coming from a Web request (e.g. the
|
# Special-case handling for filters coming from a Web request (e.g. the
|
||||||
|
@ -709,9 +718,9 @@ class CommaSeparatedIntegerField(CharField):
|
||||||
class DateField(Field):
|
class DateField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _("'%s' value has an invalid date format. It must be "
|
'invalid': _("'%(value)s' value has an invalid date format. It must be "
|
||||||
"in YYYY-MM-DD format."),
|
"in YYYY-MM-DD format."),
|
||||||
'invalid_date': _("'%s' value has the correct format (YYYY-MM-DD) "
|
'invalid_date': _("'%(value)s' value has the correct format (YYYY-MM-DD) "
|
||||||
"but it is an invalid date."),
|
"but it is an invalid date."),
|
||||||
}
|
}
|
||||||
description = _("Date (without time)")
|
description = _("Date (without time)")
|
||||||
|
@ -745,11 +754,17 @@ class DateField(Field):
|
||||||
if parsed is not None:
|
if parsed is not None:
|
||||||
return parsed
|
return parsed
|
||||||
except ValueError:
|
except ValueError:
|
||||||
msg = self.error_messages['invalid_date'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid_date'],
|
||||||
|
code='invalid_date',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
msg = self.error_messages['invalid'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid'],
|
||||||
|
code='invalid',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
def pre_save(self, model_instance, add):
|
def pre_save(self, model_instance, add):
|
||||||
if self.auto_now or (self.auto_now_add and add):
|
if self.auto_now or (self.auto_now_add and add):
|
||||||
|
@ -797,11 +812,11 @@ class DateField(Field):
|
||||||
class DateTimeField(DateField):
|
class DateTimeField(DateField):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _("'%s' value has an invalid format. It must be in "
|
'invalid': _("'%(value)s' value has an invalid format. It must be in "
|
||||||
"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."),
|
"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."),
|
||||||
'invalid_date': _("'%s' value has the correct format "
|
'invalid_date': _("'%(value)s' value has the correct format "
|
||||||
"(YYYY-MM-DD) but it is an invalid date."),
|
"(YYYY-MM-DD) but it is an invalid date."),
|
||||||
'invalid_datetime': _("'%s' value has the correct format "
|
'invalid_datetime': _("'%(value)s' value has the correct format "
|
||||||
"(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) "
|
"(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) "
|
||||||
"but it is an invalid date/time."),
|
"but it is an invalid date/time."),
|
||||||
}
|
}
|
||||||
|
@ -836,19 +851,28 @@ class DateTimeField(DateField):
|
||||||
if parsed is not None:
|
if parsed is not None:
|
||||||
return parsed
|
return parsed
|
||||||
except ValueError:
|
except ValueError:
|
||||||
msg = self.error_messages['invalid_datetime'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid_datetime'],
|
||||||
|
code='invalid_datetime',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
parsed = parse_date(value)
|
parsed = parse_date(value)
|
||||||
if parsed is not None:
|
if parsed is not None:
|
||||||
return datetime.datetime(parsed.year, parsed.month, parsed.day)
|
return datetime.datetime(parsed.year, parsed.month, parsed.day)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
msg = self.error_messages['invalid_date'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid_date'],
|
||||||
|
code='invalid_date',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
msg = self.error_messages['invalid'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid'],
|
||||||
|
code='invalid',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
def pre_save(self, model_instance, add):
|
def pre_save(self, model_instance, add):
|
||||||
if self.auto_now or (self.auto_now_add and add):
|
if self.auto_now or (self.auto_now_add and add):
|
||||||
|
@ -894,7 +918,7 @@ class DateTimeField(DateField):
|
||||||
class DecimalField(Field):
|
class DecimalField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _("'%s' value must be a decimal number."),
|
'invalid': _("'%(value)s' value must be a decimal number."),
|
||||||
}
|
}
|
||||||
description = _("Decimal number")
|
description = _("Decimal number")
|
||||||
|
|
||||||
|
@ -912,8 +936,11 @@ class DecimalField(Field):
|
||||||
try:
|
try:
|
||||||
return decimal.Decimal(value)
|
return decimal.Decimal(value)
|
||||||
except decimal.InvalidOperation:
|
except decimal.InvalidOperation:
|
||||||
msg = self.error_messages['invalid'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid'],
|
||||||
|
code='invalid',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
def _format(self, value):
|
def _format(self, value):
|
||||||
if isinstance(value, six.string_types) or value is None:
|
if isinstance(value, six.string_types) or value is None:
|
||||||
|
@ -999,7 +1026,7 @@ class FilePathField(Field):
|
||||||
class FloatField(Field):
|
class FloatField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _("'%s' value must be a float."),
|
'invalid': _("'%(value)s' value must be a float."),
|
||||||
}
|
}
|
||||||
description = _("Floating point number")
|
description = _("Floating point number")
|
||||||
|
|
||||||
|
@ -1017,8 +1044,11 @@ class FloatField(Field):
|
||||||
try:
|
try:
|
||||||
return float(value)
|
return float(value)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
msg = self.error_messages['invalid'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid'],
|
||||||
|
code='invalid',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
defaults = {'form_class': forms.FloatField}
|
defaults = {'form_class': forms.FloatField}
|
||||||
|
@ -1028,7 +1058,7 @@ class FloatField(Field):
|
||||||
class IntegerField(Field):
|
class IntegerField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _("'%s' value must be an integer."),
|
'invalid': _("'%(value)s' value must be an integer."),
|
||||||
}
|
}
|
||||||
description = _("Integer")
|
description = _("Integer")
|
||||||
|
|
||||||
|
@ -1052,8 +1082,11 @@ class IntegerField(Field):
|
||||||
try:
|
try:
|
||||||
return int(value)
|
return int(value)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
msg = self.error_messages['invalid'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid'],
|
||||||
|
code='invalid',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
defaults = {'form_class': forms.IntegerField}
|
defaults = {'form_class': forms.IntegerField}
|
||||||
|
@ -1135,7 +1168,7 @@ class GenericIPAddressField(Field):
|
||||||
class NullBooleanField(Field):
|
class NullBooleanField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _("'%s' value must be either None, True or False."),
|
'invalid': _("'%(value)s' value must be either None, True or False."),
|
||||||
}
|
}
|
||||||
description = _("Boolean (Either True, False or None)")
|
description = _("Boolean (Either True, False or None)")
|
||||||
|
|
||||||
|
@ -1158,8 +1191,11 @@ class NullBooleanField(Field):
|
||||||
return True
|
return True
|
||||||
if value in ('f', 'False', '0'):
|
if value in ('f', 'False', '0'):
|
||||||
return False
|
return False
|
||||||
msg = self.error_messages['invalid'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid'],
|
||||||
|
code='invalid',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
def get_prep_lookup(self, lookup_type, value):
|
def get_prep_lookup(self, lookup_type, value):
|
||||||
# Special-case handling for filters coming from a Web request (e.g. the
|
# Special-case handling for filters coming from a Web request (e.g. the
|
||||||
|
@ -1251,9 +1287,9 @@ class TextField(Field):
|
||||||
class TimeField(Field):
|
class TimeField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _("'%s' value has an invalid format. It must be in "
|
'invalid': _("'%(value)s' value has an invalid format. It must be in "
|
||||||
"HH:MM[:ss[.uuuuuu]] format."),
|
"HH:MM[:ss[.uuuuuu]] format."),
|
||||||
'invalid_time': _("'%s' value has the correct format "
|
'invalid_time': _("'%(value)s' value has the correct format "
|
||||||
"(HH:MM[:ss[.uuuuuu]]) but it is an invalid time."),
|
"(HH:MM[:ss[.uuuuuu]]) but it is an invalid time."),
|
||||||
}
|
}
|
||||||
description = _("Time")
|
description = _("Time")
|
||||||
|
@ -1285,11 +1321,17 @@ class TimeField(Field):
|
||||||
if parsed is not None:
|
if parsed is not None:
|
||||||
return parsed
|
return parsed
|
||||||
except ValueError:
|
except ValueError:
|
||||||
msg = self.error_messages['invalid_time'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid_time'],
|
||||||
|
code='invalid_time',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
msg = self.error_messages['invalid'] % value
|
raise exceptions.ValidationError(
|
||||||
raise exceptions.ValidationError(msg)
|
self.error_messages['invalid'],
|
||||||
|
code='invalid',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
def pre_save(self, model_instance, add):
|
def pre_save(self, model_instance, add):
|
||||||
if self.auto_now or (self.auto_now_add and add):
|
if self.auto_now or (self.auto_now_add and add):
|
||||||
|
|
|
@ -1173,8 +1173,11 @@ class ForeignKey(ForeignObject):
|
||||||
)
|
)
|
||||||
qs = qs.complex_filter(self.rel.limit_choices_to)
|
qs = qs.complex_filter(self.rel.limit_choices_to)
|
||||||
if not qs.exists():
|
if not qs.exists():
|
||||||
raise exceptions.ValidationError(self.error_messages['invalid'] % {
|
raise exceptions.ValidationError(
|
||||||
'model': self.rel.to._meta.verbose_name, 'pk': value})
|
self.error_messages['invalid'],
|
||||||
|
code='invalid',
|
||||||
|
params={'model': self.rel.to._meta.verbose_name, 'pk': value},
|
||||||
|
)
|
||||||
|
|
||||||
def get_attname(self):
|
def get_attname(self):
|
||||||
return '%s_id' % self.name
|
return '%s_id' % self.name
|
||||||
|
|
|
@ -125,7 +125,7 @@ class Field(object):
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if value in self.empty_values and self.required:
|
if value in self.empty_values and self.required:
|
||||||
raise ValidationError(self.error_messages['required'])
|
raise ValidationError(self.error_messages['required'], code='required')
|
||||||
|
|
||||||
def run_validators(self, value):
|
def run_validators(self, value):
|
||||||
if value in self.empty_values:
|
if value in self.empty_values:
|
||||||
|
@ -246,7 +246,7 @@ class IntegerField(Field):
|
||||||
try:
|
try:
|
||||||
value = int(str(value))
|
value = int(str(value))
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'], code='invalid')
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def widget_attrs(self, widget):
|
def widget_attrs(self, widget):
|
||||||
|
@ -277,7 +277,7 @@ class FloatField(IntegerField):
|
||||||
try:
|
try:
|
||||||
value = float(value)
|
value = float(value)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'], code='invalid')
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def widget_attrs(self, widget):
|
def widget_attrs(self, widget):
|
||||||
|
@ -323,7 +323,7 @@ class DecimalField(IntegerField):
|
||||||
try:
|
try:
|
||||||
value = Decimal(value)
|
value = Decimal(value)
|
||||||
except DecimalException:
|
except DecimalException:
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'], code='invalid')
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
|
@ -334,7 +334,7 @@ class DecimalField(IntegerField):
|
||||||
# since it is never equal to itself. However, NaN is the only value that
|
# since it is never equal to itself. However, NaN is the only value that
|
||||||
# isn't equal to itself, so we can use this to identify NaN
|
# isn't equal to itself, so we can use this to identify NaN
|
||||||
if value != value or value == Decimal("Inf") or value == Decimal("-Inf"):
|
if value != value or value == Decimal("Inf") or value == Decimal("-Inf"):
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'], code='invalid')
|
||||||
sign, digittuple, exponent = value.as_tuple()
|
sign, digittuple, exponent = value.as_tuple()
|
||||||
decimals = abs(exponent)
|
decimals = abs(exponent)
|
||||||
# digittuple doesn't include any leading zeros.
|
# digittuple doesn't include any leading zeros.
|
||||||
|
@ -348,15 +348,24 @@ class DecimalField(IntegerField):
|
||||||
whole_digits = digits - decimals
|
whole_digits = digits - decimals
|
||||||
|
|
||||||
if self.max_digits is not None and digits > self.max_digits:
|
if self.max_digits is not None and digits > self.max_digits:
|
||||||
raise ValidationError(self.error_messages['max_digits'] % {
|
raise ValidationError(
|
||||||
'max': self.max_digits})
|
self.error_messages['max_digits'],
|
||||||
|
code='max_digits',
|
||||||
|
params={'max': self.max_digits},
|
||||||
|
)
|
||||||
if self.decimal_places is not None and decimals > self.decimal_places:
|
if self.decimal_places is not None and decimals > self.decimal_places:
|
||||||
raise ValidationError(self.error_messages['max_decimal_places'] % {
|
raise ValidationError(
|
||||||
'max': self.decimal_places})
|
self.error_messages['max_decimal_places'],
|
||||||
|
code='max_decimal_places',
|
||||||
|
params={'max': self.decimal_places},
|
||||||
|
)
|
||||||
if (self.max_digits is not None and self.decimal_places is not None
|
if (self.max_digits is not None and self.decimal_places is not None
|
||||||
and whole_digits > (self.max_digits - self.decimal_places)):
|
and whole_digits > (self.max_digits - self.decimal_places)):
|
||||||
raise ValidationError(self.error_messages['max_whole_digits'] % {
|
raise ValidationError(
|
||||||
'max': (self.max_digits - self.decimal_places)})
|
self.error_messages['max_whole_digits'],
|
||||||
|
code='max_whole_digits',
|
||||||
|
params={'max': (self.max_digits - self.decimal_places)},
|
||||||
|
)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def widget_attrs(self, widget):
|
def widget_attrs(self, widget):
|
||||||
|
@ -391,7 +400,7 @@ class BaseTemporalField(Field):
|
||||||
return self.strptime(value, format)
|
return self.strptime(value, format)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
continue
|
continue
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'], code='invalid')
|
||||||
|
|
||||||
def strptime(self, value, format):
|
def strptime(self, value, format):
|
||||||
raise NotImplementedError('Subclasses must define this method.')
|
raise NotImplementedError('Subclasses must define this method.')
|
||||||
|
@ -471,7 +480,7 @@ class DateTimeField(BaseTemporalField):
|
||||||
# Input comes from a SplitDateTimeWidget, for example. So, it's two
|
# Input comes from a SplitDateTimeWidget, for example. So, it's two
|
||||||
# components: date and time.
|
# components: date and time.
|
||||||
if len(value) != 2:
|
if len(value) != 2:
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'], code='invalid')
|
||||||
if value[0] in self.empty_values and value[1] in self.empty_values:
|
if value[0] in self.empty_values and value[1] in self.empty_values:
|
||||||
return None
|
return None
|
||||||
value = '%s %s' % tuple(value)
|
value = '%s %s' % tuple(value)
|
||||||
|
@ -548,22 +557,22 @@ class FileField(Field):
|
||||||
file_name = data.name
|
file_name = data.name
|
||||||
file_size = data.size
|
file_size = data.size
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'], code='invalid')
|
||||||
|
|
||||||
if self.max_length is not None and len(file_name) > self.max_length:
|
if self.max_length is not None and len(file_name) > self.max_length:
|
||||||
error_values = {'max': self.max_length, 'length': len(file_name)}
|
params = {'max': self.max_length, 'length': len(file_name)}
|
||||||
raise ValidationError(self.error_messages['max_length'] % error_values)
|
raise ValidationError(self.error_messages['max_length'], code='max_length', params=params)
|
||||||
if not file_name:
|
if not file_name:
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'], code='invalid')
|
||||||
if not self.allow_empty_file and not file_size:
|
if not self.allow_empty_file and not file_size:
|
||||||
raise ValidationError(self.error_messages['empty'])
|
raise ValidationError(self.error_messages['empty'], code='empty')
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def clean(self, data, initial=None):
|
def clean(self, data, initial=None):
|
||||||
# If the widget got contradictory inputs, we raise a validation error
|
# If the widget got contradictory inputs, we raise a validation error
|
||||||
if data is FILE_INPUT_CONTRADICTION:
|
if data is FILE_INPUT_CONTRADICTION:
|
||||||
raise ValidationError(self.error_messages['contradiction'])
|
raise ValidationError(self.error_messages['contradiction'], code='contradiction')
|
||||||
# False means the field value should be cleared; further validation is
|
# False means the field value should be cleared; further validation is
|
||||||
# not needed.
|
# not needed.
|
||||||
if data is False:
|
if data is False:
|
||||||
|
@ -623,7 +632,10 @@ class ImageField(FileField):
|
||||||
Image.open(file).verify()
|
Image.open(file).verify()
|
||||||
except Exception:
|
except Exception:
|
||||||
# Pillow (or PIL) doesn't recognize it as an image.
|
# Pillow (or PIL) doesn't recognize it as an image.
|
||||||
six.reraise(ValidationError, ValidationError(self.error_messages['invalid_image']), sys.exc_info()[2])
|
six.reraise(ValidationError, ValidationError(
|
||||||
|
self.error_messages['invalid_image'],
|
||||||
|
code='invalid_image',
|
||||||
|
), sys.exc_info()[2])
|
||||||
if hasattr(f, 'seek') and callable(f.seek):
|
if hasattr(f, 'seek') and callable(f.seek):
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
return f
|
return f
|
||||||
|
@ -648,7 +660,7 @@ class URLField(CharField):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# urlparse.urlsplit can raise a ValueError with some
|
# urlparse.urlsplit can raise a ValueError with some
|
||||||
# misformatted URLs.
|
# misformatted URLs.
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'], code='invalid')
|
||||||
|
|
||||||
value = super(URLField, self).to_python(value)
|
value = super(URLField, self).to_python(value)
|
||||||
if value:
|
if value:
|
||||||
|
@ -692,7 +704,7 @@ class BooleanField(Field):
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if not value and self.required:
|
if not value and self.required:
|
||||||
raise ValidationError(self.error_messages['required'])
|
raise ValidationError(self.error_messages['required'], code='required')
|
||||||
|
|
||||||
def _has_changed(self, initial, data):
|
def _has_changed(self, initial, data):
|
||||||
# Sometimes data or initial could be None or '' which should be the
|
# Sometimes data or initial could be None or '' which should be the
|
||||||
|
@ -776,7 +788,11 @@ class ChoiceField(Field):
|
||||||
"""
|
"""
|
||||||
super(ChoiceField, self).validate(value)
|
super(ChoiceField, self).validate(value)
|
||||||
if value and not self.valid_value(value):
|
if value and not self.valid_value(value):
|
||||||
raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
|
raise ValidationError(
|
||||||
|
self.error_messages['invalid_choice'],
|
||||||
|
code='invalid_choice',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
|
|
||||||
def valid_value(self, value):
|
def valid_value(self, value):
|
||||||
"Check to see if the provided value is a valid choice"
|
"Check to see if the provided value is a valid choice"
|
||||||
|
@ -810,7 +826,11 @@ class TypedChoiceField(ChoiceField):
|
||||||
try:
|
try:
|
||||||
value = self.coerce(value)
|
value = self.coerce(value)
|
||||||
except (ValueError, TypeError, ValidationError):
|
except (ValueError, TypeError, ValidationError):
|
||||||
raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
|
raise ValidationError(
|
||||||
|
self.error_messages['invalid_choice'],
|
||||||
|
code='invalid_choice',
|
||||||
|
params={'value': value},
|
||||||
|
)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
@ -826,7 +846,7 @@ class MultipleChoiceField(ChoiceField):
|
||||||
if not value:
|
if not value:
|
||||||
return []
|
return []
|
||||||
elif not isinstance(value, (list, tuple)):
|
elif not isinstance(value, (list, tuple)):
|
||||||
raise ValidationError(self.error_messages['invalid_list'])
|
raise ValidationError(self.error_messages['invalid_list'], code='invalid_list')
|
||||||
return [smart_text(val) for val in value]
|
return [smart_text(val) for val in value]
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
|
@ -834,11 +854,15 @@ class MultipleChoiceField(ChoiceField):
|
||||||
Validates that the input is a list or tuple.
|
Validates that the input is a list or tuple.
|
||||||
"""
|
"""
|
||||||
if self.required and not value:
|
if self.required and not value:
|
||||||
raise ValidationError(self.error_messages['required'])
|
raise ValidationError(self.error_messages['required'], code='required')
|
||||||
# Validate that each value in the value list is in self.choices.
|
# Validate that each value in the value list is in self.choices.
|
||||||
for val in value:
|
for val in value:
|
||||||
if not self.valid_value(val):
|
if not self.valid_value(val):
|
||||||
raise ValidationError(self.error_messages['invalid_choice'] % {'value': val})
|
raise ValidationError(
|
||||||
|
self.error_messages['invalid_choice'],
|
||||||
|
code='invalid_choice',
|
||||||
|
params={'value': val},
|
||||||
|
)
|
||||||
|
|
||||||
def _has_changed(self, initial, data):
|
def _has_changed(self, initial, data):
|
||||||
if initial is None:
|
if initial is None:
|
||||||
|
@ -871,14 +895,18 @@ class TypedMultipleChoiceField(MultipleChoiceField):
|
||||||
try:
|
try:
|
||||||
new_value.append(self.coerce(choice))
|
new_value.append(self.coerce(choice))
|
||||||
except (ValueError, TypeError, ValidationError):
|
except (ValueError, TypeError, ValidationError):
|
||||||
raise ValidationError(self.error_messages['invalid_choice'] % {'value': choice})
|
raise ValidationError(
|
||||||
|
self.error_messages['invalid_choice'],
|
||||||
|
code='invalid_choice',
|
||||||
|
params={'value': choice},
|
||||||
|
)
|
||||||
return new_value
|
return new_value
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
if value != self.empty_value:
|
if value != self.empty_value:
|
||||||
super(TypedMultipleChoiceField, self).validate(value)
|
super(TypedMultipleChoiceField, self).validate(value)
|
||||||
elif self.required:
|
elif self.required:
|
||||||
raise ValidationError(self.error_messages['required'])
|
raise ValidationError(self.error_messages['required'], code='required')
|
||||||
|
|
||||||
|
|
||||||
class ComboField(Field):
|
class ComboField(Field):
|
||||||
|
@ -952,18 +980,18 @@ class MultiValueField(Field):
|
||||||
if not value or isinstance(value, (list, tuple)):
|
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 not value or not [v for v in value if v not in self.empty_values]:
|
||||||
if self.required:
|
if self.required:
|
||||||
raise ValidationError(self.error_messages['required'])
|
raise ValidationError(self.error_messages['required'], code='required')
|
||||||
else:
|
else:
|
||||||
return self.compress([])
|
return self.compress([])
|
||||||
else:
|
else:
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'], code='invalid')
|
||||||
for i, field in enumerate(self.fields):
|
for i, field in enumerate(self.fields):
|
||||||
try:
|
try:
|
||||||
field_value = value[i]
|
field_value = value[i]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
field_value = None
|
field_value = None
|
||||||
if self.required and field_value in self.empty_values:
|
if self.required and field_value in self.empty_values:
|
||||||
raise ValidationError(self.error_messages['required'])
|
raise ValidationError(self.error_messages['required'], code='required')
|
||||||
try:
|
try:
|
||||||
clean_data.append(field.clean(field_value))
|
clean_data.append(field.clean(field_value))
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
|
@ -1078,9 +1106,9 @@ class SplitDateTimeField(MultiValueField):
|
||||||
# Raise a validation error if time or date is empty
|
# Raise a validation error if time or date is empty
|
||||||
# (possible if SplitDateTimeField has required=False).
|
# (possible if SplitDateTimeField has required=False).
|
||||||
if data_list[0] in self.empty_values:
|
if data_list[0] in self.empty_values:
|
||||||
raise ValidationError(self.error_messages['invalid_date'])
|
raise ValidationError(self.error_messages['invalid_date'], code='invalid_date')
|
||||||
if data_list[1] in self.empty_values:
|
if data_list[1] in self.empty_values:
|
||||||
raise ValidationError(self.error_messages['invalid_time'])
|
raise ValidationError(self.error_messages['invalid_time'], code='invalid_time')
|
||||||
result = datetime.datetime.combine(*data_list)
|
result = datetime.datetime.combine(*data_list)
|
||||||
return from_current_timezone(result)
|
return from_current_timezone(result)
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -85,7 +85,10 @@ class BaseFormSet(object):
|
||||||
if self.is_bound:
|
if self.is_bound:
|
||||||
form = ManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix)
|
form = ManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix)
|
||||||
if not form.is_valid():
|
if not form.is_valid():
|
||||||
raise ValidationError('ManagementForm data is missing or has been tampered with')
|
raise ValidationError(
|
||||||
|
_('ManagementForm data is missing or has been tampered with'),
|
||||||
|
code='missing_management_form',
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
form = ManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={
|
form = ManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={
|
||||||
TOTAL_FORM_COUNT: self.total_form_count(),
|
TOTAL_FORM_COUNT: self.total_form_count(),
|
||||||
|
@ -315,7 +318,9 @@ class BaseFormSet(object):
|
||||||
self.management_form.cleaned_data[TOTAL_FORM_COUNT] > self.absolute_max:
|
self.management_form.cleaned_data[TOTAL_FORM_COUNT] > self.absolute_max:
|
||||||
raise ValidationError(ungettext(
|
raise ValidationError(ungettext(
|
||||||
"Please submit %d or fewer forms.",
|
"Please submit %d or fewer forms.",
|
||||||
"Please submit %d or fewer forms.", self.max_num) % self.max_num)
|
"Please submit %d or fewer forms.", self.max_num) % self.max_num,
|
||||||
|
code='too_many_forms',
|
||||||
|
)
|
||||||
# Give self.clean() a chance to do cross-form validation.
|
# Give self.clean() a chance to do cross-form validation.
|
||||||
self.clean()
|
self.clean()
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
|
|
|
@ -314,7 +314,17 @@ class BaseModelForm(BaseForm):
|
||||||
super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data,
|
super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data,
|
||||||
error_class, label_suffix, empty_permitted)
|
error_class, label_suffix, empty_permitted)
|
||||||
|
|
||||||
def _update_errors(self, message_dict):
|
def _update_errors(self, errors):
|
||||||
|
for field, messages in errors.error_dict.items():
|
||||||
|
if field not in self.fields:
|
||||||
|
continue
|
||||||
|
field = self.fields[field]
|
||||||
|
for message in messages:
|
||||||
|
if isinstance(message, ValidationError):
|
||||||
|
if message.code in field.error_messages:
|
||||||
|
message.message = field.error_messages[message.code]
|
||||||
|
|
||||||
|
message_dict = errors.message_dict
|
||||||
for k, v in message_dict.items():
|
for k, v in message_dict.items():
|
||||||
if k != NON_FIELD_ERRORS:
|
if k != NON_FIELD_ERRORS:
|
||||||
self._errors.setdefault(k, self.error_class()).extend(v)
|
self._errors.setdefault(k, self.error_class()).extend(v)
|
||||||
|
@ -1000,7 +1010,7 @@ class InlineForeignKeyField(Field):
|
||||||
else:
|
else:
|
||||||
orig = self.parent_instance.pk
|
orig = self.parent_instance.pk
|
||||||
if force_text(value) != force_text(orig):
|
if force_text(value) != force_text(orig):
|
||||||
raise ValidationError(self.error_messages['invalid_choice'])
|
raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')
|
||||||
return self.parent_instance
|
return self.parent_instance
|
||||||
|
|
||||||
def _has_changed(self, initial, data):
|
def _has_changed(self, initial, data):
|
||||||
|
@ -1115,7 +1125,7 @@ class ModelChoiceField(ChoiceField):
|
||||||
key = self.to_field_name or 'pk'
|
key = self.to_field_name or 'pk'
|
||||||
value = self.queryset.get(**{key: value})
|
value = self.queryset.get(**{key: value})
|
||||||
except (ValueError, self.queryset.model.DoesNotExist):
|
except (ValueError, self.queryset.model.DoesNotExist):
|
||||||
raise ValidationError(self.error_messages['invalid_choice'])
|
raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
|
@ -1150,22 +1160,30 @@ class ModelMultipleChoiceField(ModelChoiceField):
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
if self.required and not value:
|
if self.required and not value:
|
||||||
raise ValidationError(self.error_messages['required'])
|
raise ValidationError(self.error_messages['required'], code='required')
|
||||||
elif not self.required and not value:
|
elif not self.required and not value:
|
||||||
return self.queryset.none()
|
return self.queryset.none()
|
||||||
if not isinstance(value, (list, tuple)):
|
if not isinstance(value, (list, tuple)):
|
||||||
raise ValidationError(self.error_messages['list'])
|
raise ValidationError(self.error_messages['list'], code='list')
|
||||||
key = self.to_field_name or 'pk'
|
key = self.to_field_name or 'pk'
|
||||||
for pk in value:
|
for pk in value:
|
||||||
try:
|
try:
|
||||||
self.queryset.filter(**{key: pk})
|
self.queryset.filter(**{key: pk})
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValidationError(self.error_messages['invalid_pk_value'] % {'pk': pk})
|
raise ValidationError(
|
||||||
|
self.error_messages['invalid_pk_value'],
|
||||||
|
code='invalid_pk_value',
|
||||||
|
params={'pk': pk},
|
||||||
|
)
|
||||||
qs = self.queryset.filter(**{'%s__in' % key: value})
|
qs = self.queryset.filter(**{'%s__in' % key: value})
|
||||||
pks = set([force_text(getattr(o, key)) for o in qs])
|
pks = set([force_text(getattr(o, key)) for o in qs])
|
||||||
for val in value:
|
for val in value:
|
||||||
if force_text(val) not in pks:
|
if force_text(val) not in pks:
|
||||||
raise ValidationError(self.error_messages['invalid_choice'] % {'value': val})
|
raise ValidationError(
|
||||||
|
self.error_messages['invalid_choice'],
|
||||||
|
code='invalid_choice',
|
||||||
|
params={'value': val},
|
||||||
|
)
|
||||||
# Since this overrides the inherited ModelChoiceField.clean
|
# Since this overrides the inherited ModelChoiceField.clean
|
||||||
# we run custom validators here
|
# we run custom validators here
|
||||||
self.run_validators(value)
|
self.run_validators(value)
|
||||||
|
|
|
@ -80,12 +80,17 @@ def from_current_timezone(value):
|
||||||
try:
|
try:
|
||||||
return timezone.make_aware(value, current_timezone)
|
return timezone.make_aware(value, current_timezone)
|
||||||
except Exception:
|
except Exception:
|
||||||
msg = _(
|
message = _(
|
||||||
'%(datetime)s couldn\'t be interpreted '
|
'%(datetime)s couldn\'t be interpreted '
|
||||||
'in time zone %(current_timezone)s; it '
|
'in time zone %(current_timezone)s; it '
|
||||||
'may be ambiguous or it may not exist.') % {'datetime': value, 'current_timezone':
|
'may be ambiguous or it may not exist.'
|
||||||
current_timezone}
|
)
|
||||||
six.reraise(ValidationError, ValidationError(msg), sys.exc_info()[2])
|
params = {'datetime': value, 'current_timezone': current_timezone}
|
||||||
|
six.reraise(ValidationError, ValidationError(
|
||||||
|
message,
|
||||||
|
code='ambiguous_timezone',
|
||||||
|
params=params,
|
||||||
|
), sys.exc_info()[2])
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def to_current_timezone(value):
|
def to_current_timezone(value):
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
# Copyright 2007 Google Inc. http://code.google.com/p/ipaddr-py/
|
# Copyright 2007 Google Inc. http://code.google.com/p/ipaddr-py/
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License").
|
# Licensed under the Apache License, Version 2.0 (the "License").
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.six.moves import xrange
|
from django.utils.six.moves import xrange
|
||||||
|
|
||||||
def clean_ipv6_address(ip_str, unpack_ipv4=False,
|
def clean_ipv6_address(ip_str, unpack_ipv4=False,
|
||||||
error_message="This is not a valid IPv6 address."):
|
error_message=_("This is not a valid IPv6 address.")):
|
||||||
"""
|
"""
|
||||||
Cleans a IPv6 address string.
|
Cleans a IPv6 address string.
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ def clean_ipv6_address(ip_str, unpack_ipv4=False,
|
||||||
doublecolon_len = 0
|
doublecolon_len = 0
|
||||||
|
|
||||||
if not is_valid_ipv6_address(ip_str):
|
if not is_valid_ipv6_address(ip_str):
|
||||||
raise ValidationError(error_message)
|
raise ValidationError(error_message, code='invalid')
|
||||||
|
|
||||||
# This algorithm can only handle fully exploded
|
# This algorithm can only handle fully exploded
|
||||||
# IP strings
|
# IP strings
|
||||||
|
|
|
@ -12,13 +12,11 @@ validation (accessing the ``errors`` attribute or calling ``full_clean()``
|
||||||
directly), but normally they won't be needed.
|
directly), but normally they won't be needed.
|
||||||
|
|
||||||
In general, any cleaning method can raise ``ValidationError`` if there is a
|
In general, any cleaning method can raise ``ValidationError`` if there is a
|
||||||
problem with the data it is processing, passing the relevant error message to
|
problem with the data it is processing, passing the relevant information to
|
||||||
the ``ValidationError`` constructor. If no ``ValidationError`` is raised, the
|
the ``ValidationError`` constructor. :ref:`See below <raising-validation-error>`
|
||||||
method should return the cleaned (normalized) data as a Python object.
|
for the best practice in raising ``ValidationError``. If no ``ValidationError``
|
||||||
|
is raised, the method should return the cleaned (normalized) data as a Python
|
||||||
If you detect multiple errors during a cleaning method and wish to signal all
|
object.
|
||||||
of them to the form submitter, it is possible to pass a list of errors to the
|
|
||||||
``ValidationError`` constructor.
|
|
||||||
|
|
||||||
Most validation can be done using `validators`_ - simple helpers that can be
|
Most validation can be done using `validators`_ - simple helpers that can be
|
||||||
reused easily. Validators are simple functions (or callables) that take a single
|
reused easily. Validators are simple functions (or callables) that take a single
|
||||||
|
@ -87,7 +85,8 @@ overridden:
|
||||||
"field" (called ``__all__``), which you can access via the
|
"field" (called ``__all__``), which you can access via the
|
||||||
``non_field_errors()`` method if you need to. If you want to attach
|
``non_field_errors()`` method if you need to. If you want to attach
|
||||||
errors to a specific field in the form, you will need to access the
|
errors to a specific field in the form, you will need to access the
|
||||||
``_errors`` attribute on the form, which is `described later`_.
|
``_errors`` attribute on the form, which is
|
||||||
|
:ref:`described later <modifying-field-errors>`.
|
||||||
|
|
||||||
Also note that there are special considerations when overriding
|
Also note that there are special considerations when overriding
|
||||||
the ``clean()`` method of a ``ModelForm`` subclass. (see the
|
the ``clean()`` method of a ``ModelForm`` subclass. (see the
|
||||||
|
@ -116,7 +115,100 @@ should iterate through ``self.cleaned_data.items()``, possibly considering the
|
||||||
``_errors`` dictionary attribute on the form as well. In this way, you will
|
``_errors`` dictionary attribute on the form as well. In this way, you will
|
||||||
already know which fields have passed their individual validation requirements.
|
already know which fields have passed their individual validation requirements.
|
||||||
|
|
||||||
.. _described later:
|
.. _raising-validation-error:
|
||||||
|
|
||||||
|
Raising ``ValidationError``
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
.. versionchanged:: 1.6
|
||||||
|
|
||||||
|
In order to make error messages flexible and easy to override, consider the
|
||||||
|
following guidelines:
|
||||||
|
|
||||||
|
* Provide a descriptive error ``code`` to the constructor::
|
||||||
|
|
||||||
|
# Good
|
||||||
|
ValidationError(_('Invalid value'), code='invalid')
|
||||||
|
|
||||||
|
# Bad
|
||||||
|
ValidationError(_('Invalid value'))
|
||||||
|
|
||||||
|
* Don't coerce variables into the message; use placeholders and the ``params``
|
||||||
|
argument of the constructor::
|
||||||
|
|
||||||
|
# Good
|
||||||
|
ValidationError(
|
||||||
|
_('Invalid value: %(value)s'),
|
||||||
|
params={'value': '42'},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Bad
|
||||||
|
ValidationError(_('Invalid value: %s') % value)
|
||||||
|
|
||||||
|
* Use mapping keys instead of positional formatting. This enables putting
|
||||||
|
the variables in any order or omitting them altogether when rewriting the
|
||||||
|
message::
|
||||||
|
|
||||||
|
# Good
|
||||||
|
ValidationError(
|
||||||
|
_('Invalid value: %(value)s'),
|
||||||
|
params={'value': '42'},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Bad
|
||||||
|
ValidationError(
|
||||||
|
_('Invalid value: %s'),
|
||||||
|
params=('42',),
|
||||||
|
)
|
||||||
|
|
||||||
|
* Wrap the message with ``gettext`` to enable translation::
|
||||||
|
|
||||||
|
# Good
|
||||||
|
ValidationError(_('Invalid value'))
|
||||||
|
|
||||||
|
# Bad
|
||||||
|
ValidationError('Invalid value')
|
||||||
|
|
||||||
|
Putting it all together::
|
||||||
|
|
||||||
|
raise ValidationErrror(
|
||||||
|
_('Invalid value: %(value)s'),
|
||||||
|
code='invalid',
|
||||||
|
params={'value': '42'},
|
||||||
|
)
|
||||||
|
|
||||||
|
Following these guidelines is particularly necessary if you write reusable
|
||||||
|
forms, form fields, and model fields.
|
||||||
|
|
||||||
|
While not recommended, if you are at the end of the validation chain
|
||||||
|
(i.e. your form ``clean()`` method) and you know you will *never* need
|
||||||
|
to override your error message you can still opt for the less verbose::
|
||||||
|
|
||||||
|
ValidationError(_('Invalid value: %s') % value)
|
||||||
|
|
||||||
|
Raising multiple errors
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If you detect multiple errors during a cleaning method and wish to signal all
|
||||||
|
of them to the form submitter, it is possible to pass a list of errors to the
|
||||||
|
``ValidationError`` constructor.
|
||||||
|
|
||||||
|
As above, it is recommended to pass a list of ``ValidationError`` instances
|
||||||
|
with ``code``\s and ``params`` but a list of strings will also work::
|
||||||
|
|
||||||
|
# Good
|
||||||
|
raise ValidationError([
|
||||||
|
ValidationError(_('Error 1'), code='error1'),
|
||||||
|
ValidationError(_('Error 2'), code='error2'),
|
||||||
|
])
|
||||||
|
|
||||||
|
# Bad
|
||||||
|
raise ValidationError([
|
||||||
|
_('Error 1'),
|
||||||
|
_('Error 2'),
|
||||||
|
])
|
||||||
|
|
||||||
|
.. _modifying-field-errors:
|
||||||
|
|
||||||
Form subclasses and modifying field errors
|
Form subclasses and modifying field errors
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
|
@ -84,12 +84,18 @@ need to call a model's :meth:`~Model.full_clean()` method if you plan to handle
|
||||||
validation errors yourself, or if you have excluded fields from the
|
validation errors yourself, or if you have excluded fields from the
|
||||||
:class:`~django.forms.ModelForm` that require validation.
|
:class:`~django.forms.ModelForm` that require validation.
|
||||||
|
|
||||||
.. method:: Model.full_clean(exclude=None)
|
.. method:: Model.full_clean(exclude=None, validate_unique=True)
|
||||||
|
|
||||||
|
.. versionchanged:: 1.6
|
||||||
|
|
||||||
|
The ``validate_unique`` parameter was added to allow skipping
|
||||||
|
:meth:`Model.validate_unique()`. Previously, :meth:`Model.validate_unique()`
|
||||||
|
was always called by ``full_clean``.
|
||||||
|
|
||||||
This method calls :meth:`Model.clean_fields()`, :meth:`Model.clean()`, and
|
This method calls :meth:`Model.clean_fields()`, :meth:`Model.clean()`, and
|
||||||
:meth:`Model.validate_unique()`, in that order and raises a
|
:meth:`Model.validate_unique()` (if ``validate_unique`` is ``True``, in that
|
||||||
:exc:`~django.core.exceptions.ValidationError` that has a ``message_dict``
|
order and raises a :exc:`~django.core.exceptions.ValidationError` that has a
|
||||||
attribute containing errors from all three stages.
|
``message_dict`` attribute containing errors from all three stages.
|
||||||
|
|
||||||
The optional ``exclude`` argument can be used to provide a list of field names
|
The optional ``exclude`` argument can be used to provide a list of field names
|
||||||
that can be excluded from validation and cleaning.
|
that can be excluded from validation and cleaning.
|
||||||
|
|
|
@ -318,6 +318,13 @@ Minor features
|
||||||
* Formsets now have a
|
* Formsets now have a
|
||||||
:meth:`~django.forms.formsets.BaseFormSet.total_error_count` method.
|
:meth:`~django.forms.formsets.BaseFormSet.total_error_count` method.
|
||||||
|
|
||||||
|
* :class:`~django.forms.ModelForm` fields can now override error messages
|
||||||
|
defined in model fields by using the
|
||||||
|
:attr:`~django.forms.Field.error_messages` argument of a ``Field``'s
|
||||||
|
constructor. To take advantage of this new feature with your custom fields,
|
||||||
|
:ref:`see the updated recommendation <raising-validation-error>` for raising
|
||||||
|
a ``ValidationError``.
|
||||||
|
|
||||||
Backwards incompatible changes in 1.6
|
Backwards incompatible changes in 1.6
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ from __future__ import unicode_literals
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
from django.core import validators
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.files.storage import FileSystemStorage
|
from django.core.files.storage import FileSystemStorage
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -286,3 +287,12 @@ class ColourfulItem(models.Model):
|
||||||
class ArticleStatusNote(models.Model):
|
class ArticleStatusNote(models.Model):
|
||||||
name = models.CharField(max_length=20)
|
name = models.CharField(max_length=20)
|
||||||
status = models.ManyToManyField(ArticleStatus)
|
status = models.ManyToManyField(ArticleStatus)
|
||||||
|
|
||||||
|
class CustomErrorMessage(models.Model):
|
||||||
|
name1 = models.CharField(max_length=50,
|
||||||
|
validators=[validators.validate_slug],
|
||||||
|
error_messages={'invalid': 'Model custom error message.'})
|
||||||
|
|
||||||
|
name2 = models.CharField(max_length=50,
|
||||||
|
validators=[validators.validate_slug],
|
||||||
|
error_messages={'invalid': 'Model custom error message.'})
|
||||||
|
|
|
@ -22,7 +22,7 @@ from .models import (Article, ArticleStatus, BetterWriter, BigInt, Book,
|
||||||
DerivedPost, ExplicitPK, FlexibleDatePost, ImprovedArticle,
|
DerivedPost, ExplicitPK, FlexibleDatePost, ImprovedArticle,
|
||||||
ImprovedArticleWithParentLink, Inventory, Post, Price,
|
ImprovedArticleWithParentLink, Inventory, Post, Price,
|
||||||
Product, TextFile, Writer, WriterProfile, Colour, ColourfulItem,
|
Product, TextFile, Writer, WriterProfile, Colour, ColourfulItem,
|
||||||
ArticleStatusNote, DateTimePost, test_images)
|
ArticleStatusNote, DateTimePost, CustomErrorMessage, test_images)
|
||||||
|
|
||||||
if test_images:
|
if test_images:
|
||||||
from .models import ImageFile, OptionalImageFile
|
from .models import ImageFile, OptionalImageFile
|
||||||
|
@ -252,6 +252,12 @@ class StatusNoteCBM2mForm(forms.ModelForm):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
widgets = {'status': forms.CheckboxSelectMultiple}
|
widgets = {'status': forms.CheckboxSelectMultiple}
|
||||||
|
|
||||||
|
class CustomErrorMessageForm(forms.ModelForm):
|
||||||
|
name1 = forms.CharField(error_messages={'invalid': 'Form custom error message.'})
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = CustomErrorMessage
|
||||||
|
|
||||||
|
|
||||||
class ModelFormBaseTest(TestCase):
|
class ModelFormBaseTest(TestCase):
|
||||||
def test_base_form(self):
|
def test_base_form(self):
|
||||||
|
@ -1762,6 +1768,18 @@ class OldFormForXTests(TestCase):
|
||||||
</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>"""
|
</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>"""
|
||||||
% {'blue_pk': colour.pk})
|
% {'blue_pk': colour.pk})
|
||||||
|
|
||||||
|
def test_custom_error_messages(self) :
|
||||||
|
data = {'name1': '@#$!!**@#$', 'name2': '@#$!!**@#$'}
|
||||||
|
errors = CustomErrorMessageForm(data).errors
|
||||||
|
self.assertHTMLEqual(
|
||||||
|
str(errors['name1']),
|
||||||
|
'<ul class="errorlist"><li>Form custom error message.</li></ul>'
|
||||||
|
)
|
||||||
|
self.assertHTMLEqual(
|
||||||
|
str(errors['name2']),
|
||||||
|
'<ul class="errorlist"><li>Model custom error message.</li></ul>'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class M2mHelpTextTest(TestCase):
|
class M2mHelpTextTest(TestCase):
|
||||||
"""Tests for ticket #9321."""
|
"""Tests for ticket #9321."""
|
||||||
|
|
|
@ -214,8 +214,8 @@ class TestSimpleValidators(TestCase):
|
||||||
|
|
||||||
def test_message_dict(self):
|
def test_message_dict(self):
|
||||||
v = ValidationError({'first': ['First Problem']})
|
v = ValidationError({'first': ['First Problem']})
|
||||||
self.assertEqual(str(v), str_prefix("{%(_)s'first': %(_)s'First Problem'}"))
|
self.assertEqual(str(v), str_prefix("{%(_)s'first': [%(_)s'First Problem']}"))
|
||||||
self.assertEqual(repr(v), str_prefix("ValidationError({%(_)s'first': %(_)s'First Problem'})"))
|
self.assertEqual(repr(v), str_prefix("ValidationError({%(_)s'first': [%(_)s'First Problem']})"))
|
||||||
|
|
||||||
test_counter = 0
|
test_counter = 0
|
||||||
for validator, value, expected in TEST_DATA:
|
for validator, value, expected in TEST_DATA:
|
||||||
|
|
Loading…
Reference in New Issue