Fixed #3457 -- Allow overridding of error messages for newforms Fields.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@6625 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Gary Wilson Jr 2007-10-28 05:40:26 +00:00
parent ee49e934d9
commit 26ea06b0ab
7 changed files with 533 additions and 54 deletions

View File

@ -16,7 +16,7 @@ try:
except NameError: except NameError:
from sets import Set as set from sets import Set as set
from django.utils.translation import ugettext from django.utils.translation import ugettext_lazy
from django.utils.encoding import StrAndUnicode, smart_unicode from django.utils.encoding import StrAndUnicode, smart_unicode
from util import ErrorList, ValidationError from util import ErrorList, ValidationError
@ -41,11 +41,16 @@ EMPTY_VALUES = (None, '')
class Field(object): class Field(object):
widget = TextInput # Default widget to use when rendering this type of Field. widget = TextInput # Default widget to use when rendering this type of Field.
hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden". hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
default_error_messages = {
'required': ugettext_lazy(u'This field is required.'),
'invalid': ugettext_lazy(u'Enter a valid value.'),
}
# Tracks each time a Field instance is created. Used to retain order. # Tracks each time a Field instance is created. Used to retain order.
creation_counter = 0 creation_counter = 0
def __init__(self, required=True, widget=None, label=None, initial=None, help_text=None): def __init__(self, required=True, widget=None, label=None, initial=None,
help_text=None, error_messages=None):
# required -- Boolean that specifies whether the field is required. # required -- Boolean that specifies whether the field is required.
# True by default. # True by default.
# widget -- A Widget class, or instance of a Widget class, that should # widget -- A Widget class, or instance of a Widget class, that should
@ -78,6 +83,20 @@ class Field(object):
self.creation_counter = Field.creation_counter self.creation_counter = Field.creation_counter
Field.creation_counter += 1 Field.creation_counter += 1
self.error_messages = self._build_error_messages(error_messages)
def _build_error_messages(self, extra_error_messages):
error_messages = {}
def get_default_error_messages(klass):
for base_class in klass.__bases__:
get_default_error_messages(base_class)
if hasattr(klass, 'default_error_messages'):
error_messages.update(klass.default_error_messages)
get_default_error_messages(self.__class__)
if extra_error_messages:
error_messages.update(extra_error_messages)
return error_messages
def clean(self, value): def clean(self, value):
""" """
Validates the given value and returns its "cleaned" value as an Validates the given value and returns its "cleaned" value as an
@ -86,7 +105,7 @@ class Field(object):
Raises ValidationError for any errors. Raises ValidationError for any errors.
""" """
if self.required and value in EMPTY_VALUES: if self.required and value in EMPTY_VALUES:
raise ValidationError(ugettext(u'This field is required.')) raise ValidationError(self.error_messages['required'])
return value return value
def widget_attrs(self, widget): def widget_attrs(self, widget):
@ -104,6 +123,11 @@ class Field(object):
return result return result
class CharField(Field): class CharField(Field):
default_error_messages = {
'max_length': ugettext_lazy(u'Ensure this value has at most %(max)d characters (it has %(length)d).'),
'min_length': ugettext_lazy(u'Ensure this value has at least %(min)d characters (it has %(length)d).'),
}
def __init__(self, max_length=None, min_length=None, *args, **kwargs): def __init__(self, max_length=None, min_length=None, *args, **kwargs):
self.max_length, self.min_length = max_length, min_length self.max_length, self.min_length = max_length, min_length
super(CharField, self).__init__(*args, **kwargs) super(CharField, self).__init__(*args, **kwargs)
@ -116,9 +140,9 @@ class CharField(Field):
value = smart_unicode(value) value = smart_unicode(value)
value_length = len(value) value_length = len(value)
if self.max_length is not None and value_length > self.max_length: if self.max_length is not None and value_length > self.max_length:
raise ValidationError(ugettext(u'Ensure this value has at most %(max)d characters (it has %(length)d).') % {'max': self.max_length, 'length': value_length}) raise ValidationError(self.error_messages['max_length'] % {'max': self.max_length, 'length': value_length})
if self.min_length is not None and value_length < self.min_length: if self.min_length is not None and value_length < self.min_length:
raise ValidationError(ugettext(u'Ensure this value has at least %(min)d characters (it has %(length)d).') % {'min': self.min_length, 'length': value_length}) raise ValidationError(self.error_messages['min_length'] % {'min': self.min_length, 'length': value_length})
return value return value
def widget_attrs(self, widget): def widget_attrs(self, widget):
@ -127,6 +151,12 @@ class CharField(Field):
return {'maxlength': str(self.max_length)} return {'maxlength': str(self.max_length)}
class IntegerField(Field): class IntegerField(Field):
default_error_messages = {
'invalid': ugettext_lazy(u'Enter a whole number.'),
'max_value': ugettext_lazy(u'Ensure this value is less than or equal to %s.'),
'min_value': ugettext_lazy(u'Ensure this value is greater than or equal to %s.'),
}
def __init__(self, max_value=None, min_value=None, *args, **kwargs): def __init__(self, max_value=None, min_value=None, *args, **kwargs):
self.max_value, self.min_value = max_value, min_value self.max_value, self.min_value = max_value, min_value
super(IntegerField, self).__init__(*args, **kwargs) super(IntegerField, self).__init__(*args, **kwargs)
@ -142,14 +172,20 @@ class IntegerField(Field):
try: try:
value = int(str(value)) value = int(str(value))
except (ValueError, TypeError): except (ValueError, TypeError):
raise ValidationError(ugettext(u'Enter a whole number.')) raise ValidationError(self.error_messages['invalid'])
if self.max_value is not None and value > self.max_value: if self.max_value is not None and value > self.max_value:
raise ValidationError(ugettext(u'Ensure this value is less than or equal to %s.') % self.max_value) raise ValidationError(self.error_messages['max_value'] % self.max_value)
if self.min_value is not None and value < self.min_value: if self.min_value is not None and value < self.min_value:
raise ValidationError(ugettext(u'Ensure this value is greater than or equal to %s.') % self.min_value) raise ValidationError(self.error_messages['min_value'] % self.min_value)
return value return value
class FloatField(Field): class FloatField(Field):
default_error_messages = {
'invalid': ugettext_lazy(u'Enter a number.'),
'max_value': ugettext_lazy(u'Ensure this value is less than or equal to %s.'),
'min_value': ugettext_lazy(u'Ensure this value is greater than or equal to %s.'),
}
def __init__(self, max_value=None, min_value=None, *args, **kwargs): def __init__(self, max_value=None, min_value=None, *args, **kwargs):
self.max_value, self.min_value = max_value, min_value self.max_value, self.min_value = max_value, min_value
Field.__init__(self, *args, **kwargs) Field.__init__(self, *args, **kwargs)
@ -165,14 +201,23 @@ class FloatField(Field):
try: try:
value = float(value) value = float(value)
except (ValueError, TypeError): except (ValueError, TypeError):
raise ValidationError(ugettext('Enter a number.')) raise ValidationError(self.error_messages['invalid'])
if self.max_value is not None and value > self.max_value: if self.max_value is not None and value > self.max_value:
raise ValidationError(ugettext('Ensure this value is less than or equal to %s.') % self.max_value) raise ValidationError(self.error_messages['max_value'] % self.max_value)
if self.min_value is not None and value < self.min_value: if self.min_value is not None and value < self.min_value:
raise ValidationError(ugettext('Ensure this value is greater than or equal to %s.') % self.min_value) raise ValidationError(self.error_messages['min_value'] % self.min_value)
return value return value
class DecimalField(Field): class DecimalField(Field):
default_error_messages = {
'invalid': ugettext_lazy(u'Enter a number.'),
'max_value': ugettext_lazy(u'Ensure this value is less than or equal to %s.'),
'min_value': ugettext_lazy(u'Ensure this value is greater than or equal to %s.'),
'max_digits': ugettext_lazy('Ensure that there are no more than %s digits in total.'),
'max_decimal_places': ugettext_lazy('Ensure that there are no more than %s decimal places.'),
'max_whole_digits': ugettext_lazy('Ensure that there are no more than %s digits before the decimal point.')
}
def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs): def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs):
self.max_value, self.min_value = max_value, min_value self.max_value, self.min_value = max_value, min_value
self.max_digits, self.decimal_places = max_digits, decimal_places self.max_digits, self.decimal_places = max_digits, decimal_places
@ -192,20 +237,20 @@ class DecimalField(Field):
try: try:
value = Decimal(value) value = Decimal(value)
except DecimalException: except DecimalException:
raise ValidationError(ugettext('Enter a number.')) raise ValidationError(self.error_messages['invalid'])
pieces = str(value).lstrip("-").split('.') pieces = str(value).lstrip("-").split('.')
decimals = (len(pieces) == 2) and len(pieces[1]) or 0 decimals = (len(pieces) == 2) and len(pieces[1]) or 0
digits = len(pieces[0]) digits = len(pieces[0])
if self.max_value is not None and value > self.max_value: if self.max_value is not None and value > self.max_value:
raise ValidationError(ugettext('Ensure this value is less than or equal to %s.') % self.max_value) raise ValidationError(self.error_messages['max_value'] % self.max_value)
if self.min_value is not None and value < self.min_value: if self.min_value is not None and value < self.min_value:
raise ValidationError(ugettext('Ensure this value is greater than or equal to %s.') % self.min_value) raise ValidationError(self.error_messages['min_value'] % self.min_value)
if self.max_digits is not None and (digits + decimals) > self.max_digits: if self.max_digits is not None and (digits + decimals) > self.max_digits:
raise ValidationError(ugettext('Ensure that there are no more than %s digits in total.') % self.max_digits) raise ValidationError(self.error_messages['max_digits'] % 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(ugettext('Ensure that there are no more than %s decimal places.') % self.decimal_places) raise ValidationError(self.error_messages['max_decimal_places'] % self.decimal_places)
if self.max_digits is not None and self.decimal_places is not None and digits > (self.max_digits - self.decimal_places): if self.max_digits is not None and self.decimal_places is not None and digits > (self.max_digits - self.decimal_places):
raise ValidationError(ugettext('Ensure that there are no more than %s digits before the decimal point.') % (self.max_digits - self.decimal_places)) raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
return value return value
DEFAULT_DATE_INPUT_FORMATS = ( DEFAULT_DATE_INPUT_FORMATS = (
@ -217,6 +262,10 @@ DEFAULT_DATE_INPUT_FORMATS = (
) )
class DateField(Field): class DateField(Field):
default_error_messages = {
'invalid': ugettext_lazy(u'Enter a valid date.'),
}
def __init__(self, input_formats=None, *args, **kwargs): def __init__(self, input_formats=None, *args, **kwargs):
super(DateField, self).__init__(*args, **kwargs) super(DateField, self).__init__(*args, **kwargs)
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
@ -238,7 +287,7 @@ class DateField(Field):
return datetime.date(*time.strptime(value, format)[:3]) return datetime.date(*time.strptime(value, format)[:3])
except ValueError: except ValueError:
continue continue
raise ValidationError(ugettext(u'Enter a valid date.')) raise ValidationError(self.error_messages['invalid'])
DEFAULT_TIME_INPUT_FORMATS = ( DEFAULT_TIME_INPUT_FORMATS = (
'%H:%M:%S', # '14:30:59' '%H:%M:%S', # '14:30:59'
@ -246,6 +295,10 @@ DEFAULT_TIME_INPUT_FORMATS = (
) )
class TimeField(Field): class TimeField(Field):
default_error_messages = {
'invalid': ugettext_lazy(u'Enter a valid time.')
}
def __init__(self, input_formats=None, *args, **kwargs): def __init__(self, input_formats=None, *args, **kwargs):
super(TimeField, self).__init__(*args, **kwargs) super(TimeField, self).__init__(*args, **kwargs)
self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
@ -265,7 +318,7 @@ class TimeField(Field):
return datetime.time(*time.strptime(value, format)[3:6]) return datetime.time(*time.strptime(value, format)[3:6])
except ValueError: except ValueError:
continue continue
raise ValidationError(ugettext(u'Enter a valid time.')) raise ValidationError(self.error_messages['invalid'])
DEFAULT_DATETIME_INPUT_FORMATS = ( DEFAULT_DATETIME_INPUT_FORMATS = (
'%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
@ -281,6 +334,9 @@ DEFAULT_DATETIME_INPUT_FORMATS = (
class DateTimeField(Field): class DateTimeField(Field):
widget = DateTimeInput widget = DateTimeInput
default_error_messages = {
'invalid': ugettext_lazy(u'Enter a valid date/time.'),
}
def __init__(self, input_formats=None, *args, **kwargs): def __init__(self, input_formats=None, *args, **kwargs):
super(DateTimeField, self).__init__(*args, **kwargs) super(DateTimeField, self).__init__(*args, **kwargs)
@ -302,14 +358,14 @@ class DateTimeField(Field):
# 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(ugettext(u'Enter a valid date/time.')) raise ValidationError(self.error_messages['invalid'])
value = '%s %s' % tuple(value) value = '%s %s' % tuple(value)
for format in self.input_formats: for format in self.input_formats:
try: try:
return datetime.datetime(*time.strptime(value, format)[:6]) return datetime.datetime(*time.strptime(value, format)[:6])
except ValueError: except ValueError:
continue continue
raise ValidationError(ugettext(u'Enter a valid date/time.')) raise ValidationError(self.error_messages['invalid'])
class RegexField(CharField): class RegexField(CharField):
def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs): def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
@ -318,11 +374,15 @@ class RegexField(CharField):
error_message is an optional error message to use, if error_message is an optional error message to use, if
'Enter a valid value' is too generic for you. 'Enter a valid value' is too generic for you.
""" """
# error_message is just kept for backwards compatibility:
if error_message:
error_messages = kwargs.get('error_messages') or {}
error_messages['invalid'] = error_message
kwargs['error_messages'] = error_messages
super(RegexField, self).__init__(max_length, min_length, *args, **kwargs) super(RegexField, self).__init__(max_length, min_length, *args, **kwargs)
if isinstance(regex, basestring): if isinstance(regex, basestring):
regex = re.compile(regex) regex = re.compile(regex)
self.regex = regex self.regex = regex
self.error_message = error_message or ugettext(u'Enter a valid value.')
def clean(self, value): def clean(self, value):
""" """
@ -333,7 +393,7 @@ class RegexField(CharField):
if value == u'': if value == u'':
return value return value
if not self.regex.search(value): if not self.regex.search(value):
raise ValidationError(self.error_message) raise ValidationError(self.error_messages['invalid'])
return value return value
email_re = re.compile( email_re = re.compile(
@ -342,9 +402,13 @@ email_re = re.compile(
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
class EmailField(RegexField): class EmailField(RegexField):
default_error_messages = {
'invalid': ugettext_lazy(u'Enter a valid e-mail address.'),
}
def __init__(self, max_length=None, min_length=None, *args, **kwargs): def __init__(self, max_length=None, min_length=None, *args, **kwargs):
RegexField.__init__(self, email_re, max_length, min_length, RegexField.__init__(self, email_re, max_length, min_length, *args,
ugettext(u'Enter a valid e-mail address.'), *args, **kwargs) **kwargs)
try: try:
from django.conf import settings from django.conf import settings
@ -368,6 +432,12 @@ class UploadedFile(StrAndUnicode):
class FileField(Field): class FileField(Field):
widget = FileInput widget = FileInput
default_error_messages = {
'invalid': ugettext_lazy(u"No file was submitted. Check the encoding type on the form."),
'missing': ugettext_lazy(u"No file was submitted."),
'empty': ugettext_lazy(u"The submitted file is empty."),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(FileField, self).__init__(*args, **kwargs) super(FileField, self).__init__(*args, **kwargs)
@ -378,14 +448,18 @@ class FileField(Field):
try: try:
f = UploadedFile(data['filename'], data['content']) f = UploadedFile(data['filename'], data['content'])
except TypeError: except TypeError:
raise ValidationError(ugettext(u"No file was submitted. Check the encoding type on the form.")) raise ValidationError(self.error_messages['invalid'])
except KeyError: except KeyError:
raise ValidationError(ugettext(u"No file was submitted.")) raise ValidationError(self.error_messages['missing'])
if not f.content: if not f.content:
raise ValidationError(ugettext(u"The submitted file is empty.")) raise ValidationError(self.error_messages['empty'])
return f return f
class ImageField(FileField): class ImageField(FileField):
default_error_messages = {
'invalid_image': ugettext_lazy(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."),
}
def clean(self, data): def clean(self, data):
""" """
Checks that the file-upload field data contains a valid image (GIF, JPG, Checks that the file-upload field data contains a valid image (GIF, JPG,
@ -406,7 +480,7 @@ class ImageField(FileField):
trial_image = Image.open(StringIO(f.content)) trial_image = Image.open(StringIO(f.content))
trial_image.verify() trial_image.verify()
except Exception: # Python Imaging Library doesn't recognize it as an image except Exception: # Python Imaging Library doesn't recognize it as an image
raise ValidationError(ugettext(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image.")) raise ValidationError(self.error_messages['invalid_image'])
return f return f
url_re = re.compile( url_re = re.compile(
@ -418,9 +492,15 @@ url_re = re.compile(
r'(?:/?|/\S+)$', re.IGNORECASE) r'(?:/?|/\S+)$', re.IGNORECASE)
class URLField(RegexField): class URLField(RegexField):
default_error_messages = {
'invalid': ugettext_lazy(u'Enter a valid URL.'),
'invalid_link': ugettext_lazy(u'This URL appears to be a broken link.'),
}
def __init__(self, max_length=None, min_length=None, verify_exists=False, def __init__(self, max_length=None, min_length=None, verify_exists=False,
validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs): validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):
super(URLField, self).__init__(url_re, max_length, min_length, ugettext(u'Enter a valid URL.'), *args, **kwargs) super(URLField, self).__init__(url_re, max_length, min_length, *args,
**kwargs)
self.verify_exists = verify_exists self.verify_exists = verify_exists
self.user_agent = validator_user_agent self.user_agent = validator_user_agent
@ -445,9 +525,9 @@ class URLField(RegexField):
req = urllib2.Request(value, None, headers) req = urllib2.Request(value, None, headers)
u = urllib2.urlopen(req) u = urllib2.urlopen(req)
except ValueError: except ValueError:
raise ValidationError(ugettext(u'Enter a valid URL.')) raise ValidationError(self.error_messages['invalid'])
except: # urllib2.URLError, httplib.InvalidURL, etc. except: # urllib2.URLError, httplib.InvalidURL, etc.
raise ValidationError(ugettext(u'This URL appears to be a broken link.')) raise ValidationError(self.error_messages['invalid_link'])
return value return value
class BooleanField(Field): class BooleanField(Field):
@ -474,9 +554,14 @@ class NullBooleanField(BooleanField):
class ChoiceField(Field): class ChoiceField(Field):
widget = Select widget = Select
default_error_messages = {
'invalid_choice': ugettext_lazy(u'Select a valid choice. That choice is not one of the available choices.'),
}
def __init__(self, choices=(), required=True, widget=None, label=None, initial=None, help_text=None): def __init__(self, choices=(), required=True, widget=None, label=None,
super(ChoiceField, self).__init__(required, widget, label, initial, help_text) initial=None, help_text=None, *args, **kwargs):
super(ChoiceField, self).__init__(required, widget, label, initial,
help_text, *args, **kwargs)
self.choices = choices self.choices = choices
def _get_choices(self): def _get_choices(self):
@ -502,29 +587,33 @@ class ChoiceField(Field):
return value return value
valid_values = set([smart_unicode(k) for k, v in self.choices]) valid_values = set([smart_unicode(k) for k, v in self.choices])
if value not in valid_values: if value not in valid_values:
raise ValidationError(ugettext(u'Select a valid choice. That choice is not one of the available choices.')) raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
return value return value
class MultipleChoiceField(ChoiceField): class MultipleChoiceField(ChoiceField):
hidden_widget = MultipleHiddenInput hidden_widget = MultipleHiddenInput
widget = SelectMultiple widget = SelectMultiple
default_error_messages = {
'invalid_choice': ugettext_lazy(u'Select a valid choice. %(value)s is not one of the available choices.'),
'invalid_list': ugettext_lazy(u'Enter a list of values.'),
}
def clean(self, value): def clean(self, value):
""" """
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(ugettext(u'This field is required.')) raise ValidationError(self.error_messages['required'])
elif not self.required and not value: elif not self.required and not value:
return [] return []
if not isinstance(value, (list, tuple)): if not isinstance(value, (list, tuple)):
raise ValidationError(ugettext(u'Enter a list of values.')) raise ValidationError(self.error_messages['invalid_list'])
new_value = [smart_unicode(val) for val in value] new_value = [smart_unicode(val) for val in value]
# Validate that each value in the value list is in self.choices. # Validate that each value in the value list is in self.choices.
valid_values = set([smart_unicode(k) for k, v in self.choices]) valid_values = set([smart_unicode(k) for k, v in self.choices])
for val in new_value: for val in new_value:
if val not in valid_values: if val not in valid_values:
raise ValidationError(ugettext(u'Select a valid choice. %s is not one of the available choices.') % val) raise ValidationError(self.error_messages['invalid_choice'] % {'value': val})
return new_value return new_value
class ComboField(Field): class ComboField(Field):
@ -567,6 +656,10 @@ class MultiValueField(Field):
You'll probably want to use this with MultiWidget. You'll probably want to use this with MultiWidget.
""" """
default_error_messages = {
'invalid': ugettext_lazy(u'Enter a list of values.'),
}
def __init__(self, fields=(), *args, **kwargs): def __init__(self, fields=(), *args, **kwargs):
super(MultiValueField, self).__init__(*args, **kwargs) super(MultiValueField, self).__init__(*args, **kwargs)
# Set 'required' to False on the individual fields, because the # Set 'required' to False on the individual fields, because the
@ -590,18 +683,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 EMPTY_VALUES]: if not value or not [v for v in value if v not in EMPTY_VALUES]:
if self.required: if self.required:
raise ValidationError(ugettext(u'This field is required.')) raise ValidationError(self.error_messages['required'])
else: else:
return self.compress([]) return self.compress([])
else: else:
raise ValidationError(ugettext(u'Enter a list of values.')) raise ValidationError(self.error_messages['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 EMPTY_VALUES: if self.required and field_value in EMPTY_VALUES:
raise ValidationError(ugettext(u'This field is required.')) raise ValidationError(self.error_messages['required'])
try: try:
clean_data.append(field.clean(field_value)) clean_data.append(field.clean(field_value))
except ValidationError, e: except ValidationError, e:
@ -625,8 +718,19 @@ class MultiValueField(Field):
raise NotImplementedError('Subclasses must implement this method.') raise NotImplementedError('Subclasses must implement this method.')
class SplitDateTimeField(MultiValueField): class SplitDateTimeField(MultiValueField):
default_error_messages = {
'invalid_date': ugettext_lazy(u'Enter a valid date.'),
'invalid_time': ugettext_lazy(u'Enter a valid time.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
fields = (DateField(), TimeField()) errors = self.default_error_messages.copy()
if 'error_messages' in kwargs:
errors.update(kwargs['error_messages'])
fields = (
DateField(error_messages={'invalid': errors['invalid_date']}),
TimeField(error_messages={'invalid': errors['invalid_time']}),
)
super(SplitDateTimeField, self).__init__(fields, *args, **kwargs) super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
def compress(self, data_list): def compress(self, data_list):
@ -634,16 +738,18 @@ 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 EMPTY_VALUES: if data_list[0] in EMPTY_VALUES:
raise ValidationError(ugettext(u'Enter a valid date.')) raise ValidationError(self.error_messages['invalid_date'])
if data_list[1] in EMPTY_VALUES: if data_list[1] in EMPTY_VALUES:
raise ValidationError(ugettext(u'Enter a valid time.')) raise ValidationError(self.error_messages['invalid_time'])
return datetime.datetime.combine(*data_list) return datetime.datetime.combine(*data_list)
return None return None
ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
class IPAddressField(RegexField): class IPAddressField(RegexField):
default_error_messages = {
'invalid': ugettext_lazy(u'Enter a valid IPv4 address.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
RegexField.__init__(self, ipv4_re, super(IPAddressField, self).__init__(ipv4_re, *args, **kwargs)
error_message=ugettext(u'Enter a valid IPv4 address.'),
*args, **kwargs)

View File

@ -42,13 +42,18 @@ class ErrorList(list, StrAndUnicode):
if not self: return u'' if not self: return u''
return u'\n'.join([u'* %s' % force_unicode(e) for e in self]) return u'\n'.join([u'* %s' % force_unicode(e) for e in self])
def __repr__(self):
return repr([force_unicode(e) for e in self])
class ValidationError(Exception): class ValidationError(Exception):
def __init__(self, message): def __init__(self, message):
"ValidationError can be passed a string or a list." """
ValidationError can be passed any object that can be printed (usually
a string) or a list of objects.
"""
if isinstance(message, list): if isinstance(message, list):
self.messages = ErrorList([smart_unicode(msg) for msg in message]) self.messages = ErrorList([smart_unicode(msg) for msg in message])
else: else:
assert isinstance(message, (basestring, Promise)), ("%s should be a basestring or lazy translation" % repr(message))
message = smart_unicode(message) message = smart_unicode(message)
self.messages = ErrorList([message]) self.messages = ErrorList([message])
@ -58,4 +63,3 @@ class ValidationError(Exception):
# AttributeError: ValidationError instance has no attribute 'args' # AttributeError: ValidationError instance has no attribute 'args'
# See http://www.python.org/doc/current/tut/node10.html#handling # See http://www.python.org/doc/current/tut/node10.html#handling
return repr(self.messages) return repr(self.messages)

View File

@ -146,7 +146,7 @@ class TestCase(unittest.TestCase):
" context %d does not contain the" " context %d does not contain the"
" error '%s' (actual errors: %s)" % " error '%s' (actual errors: %s)" %
(field, form, i, err, (field, form, i, err,
list(field_errors))) repr(field_errors)))
elif field in context[form].fields: elif field in context[form].fields:
self.fail("The field '%s' on form '%s' in context %d" self.fail("The field '%s' on form '%s' in context %d"
" contains no errors" % (field, form, i)) " contains no errors" % (field, form, i))

View File

@ -1078,6 +1078,30 @@ fields. We've specified ``auto_id=False`` to simplify the output::
<p>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</p> <p>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</p>
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p> <p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
``error_messages``
~~~~~~~~~~~~~~~~~~
**New in Django development version**
The ``error_messages`` argument lets you override the default messages which the
field will raise. Pass in a dictionary with keys matching the error messages you
want to override. For example::
>>> generic = forms.CharField()
>>> generic.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> name = forms.CharField(error_messages={'required': 'Please enter your name'})
>>> name.clean('')
Traceback (most recent call last):
...
ValidationError: [u'Please enter your name']
In the `built-in Field classes`_ section below, each Field defines the error
message keys it uses.
Dynamic initial values Dynamic initial values
---------------------- ----------------------
@ -1143,6 +1167,7 @@ For each field, we describe the default widget used if you don't specify
* Normalizes to: A Python ``True`` or ``False`` value. * Normalizes to: A Python ``True`` or ``False`` value.
* Validates that the check box is checked (i.e. the value is ``True``) if * Validates that the check box is checked (i.e. the value is ``True``) if
the field has ``required=True``. the field has ``required=True``.
* Error message keys: ``required``
**New in Django development version:** The empty value for a ``CheckboxInput`` **New in Django development version:** The empty value for a ``CheckboxInput``
(and hence the standard ``BooleanField``) has changed to return ``False`` (and hence the standard ``BooleanField``) has changed to return ``False``
@ -1162,6 +1187,7 @@ instead of ``None`` in the development version.
* Normalizes to: A Unicode object. * Normalizes to: A Unicode object.
* Validates ``max_length`` or ``min_length``, if they are provided. * Validates ``max_length`` or ``min_length``, if they are provided.
Otherwise, all inputs are valid. Otherwise, all inputs are valid.
* Error message keys: ``required``, ``max_length``, ``min_length``
Has two optional arguments for validation, ``max_length`` and ``min_length``. Has two optional arguments for validation, ``max_length`` and ``min_length``.
If provided, these arguments ensure that the string is at most or at least the If provided, these arguments ensure that the string is at most or at least the
@ -1174,6 +1200,7 @@ given length.
* Empty value: ``''`` (an empty string) * Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object. * Normalizes to: A Unicode object.
* Validates that the given value exists in the list of choices. * Validates that the given value exists in the list of choices.
* Error message keys: ``required``, ``invalid_choice``
Takes one extra argument, ``choices``, which is an iterable (e.g., a list or Takes one extra argument, ``choices``, which is an iterable (e.g., a list or
tuple) of 2-tuples to use as choices for this field. tuple) of 2-tuples to use as choices for this field.
@ -1186,6 +1213,7 @@ tuple) of 2-tuples to use as choices for this field.
* Normalizes to: A Python ``datetime.date`` object. * Normalizes to: A Python ``datetime.date`` object.
* Validates that the given value is either a ``datetime.date``, * Validates that the given value is either a ``datetime.date``,
``datetime.datetime`` or string formatted in a particular date format. ``datetime.datetime`` or string formatted in a particular date format.
* Error message keys: ``required``, ``invalid``
Takes one optional argument, ``input_formats``, which is a list of formats used Takes one optional argument, ``input_formats``, which is a list of formats used
to attempt to convert a string to a valid ``datetime.date`` object. to attempt to convert a string to a valid ``datetime.date`` object.
@ -1206,6 +1234,7 @@ If no ``input_formats`` argument is provided, the default input formats are::
* Normalizes to: A Python ``datetime.datetime`` object. * Normalizes to: A Python ``datetime.datetime`` object.
* Validates that the given value is either a ``datetime.datetime``, * Validates that the given value is either a ``datetime.datetime``,
``datetime.date`` or string formatted in a particular datetime format. ``datetime.date`` or string formatted in a particular datetime format.
* Error message keys: ``required``, ``invalid``
Takes one optional argument, ``input_formats``, which is a list of formats used Takes one optional argument, ``input_formats``, which is a list of formats used
to attempt to convert a string to a valid ``datetime.datetime`` object. to attempt to convert a string to a valid ``datetime.datetime`` object.
@ -1235,6 +1264,9 @@ If no ``input_formats`` argument is provided, the default input formats are::
* Normalizes to: A Python ``decimal``. * Normalizes to: A Python ``decimal``.
* Validates that the given value is a decimal. Leading and trailing * Validates that the given value is a decimal. Leading and trailing
whitespace is ignored. whitespace is ignored.
* Error message keys: ``required``, ``invalid``, ``max_value``,
``min_value``, ``max_digits``, ``max_decimal_places``,
``max_whole_digits``
Takes four optional arguments: ``max_value``, ``min_value``, ``max_digits``, Takes four optional arguments: ``max_value``, ``min_value``, ``max_digits``,
and ``decimal_places``. The first two define the limits for the fields value. and ``decimal_places``. The first two define the limits for the fields value.
@ -1251,6 +1283,7 @@ decimal places permitted.
* Normalizes to: A Unicode object. * Normalizes to: A Unicode object.
* Validates that the given value is a valid e-mail address, using a * Validates that the given value is a valid e-mail address, using a
moderately complex regular expression. moderately complex regular expression.
* Error message keys: ``required``, ``invalid``
Has two optional arguments for validation, ``max_length`` and ``min_length``. Has two optional arguments for validation, ``max_length`` and ``min_length``.
If provided, these arguments ensure that the string is at most or at least the If provided, these arguments ensure that the string is at most or at least the
@ -1266,6 +1299,7 @@ given length.
* Normalizes to: An ``UploadedFile`` object that wraps the file content * Normalizes to: An ``UploadedFile`` object that wraps the file content
and file name into a single object. and file name into a single object.
* Validates that non-empty file data has been bound to the form. * Validates that non-empty file data has been bound to the form.
* Error message keys: ``required``, ``invalid``, ``missing``, ``empty``
An ``UploadedFile`` object has two attributes: An ``UploadedFile`` object has two attributes:
@ -1296,6 +1330,8 @@ When you use a ``FileField`` on a form, you must also remember to
and file name into a single object. and file name into a single object.
* Validates that file data has been bound to the form, and that the * Validates that file data has been bound to the form, and that the
file is of an image format understood by PIL. file is of an image format understood by PIL.
* Error message keys: ``required``, ``invalid``, ``missing``, ``empty``,
``invalid_image``
Using an ImageField requires that the `Python Imaging Library`_ is installed. Using an ImageField requires that the `Python Imaging Library`_ is installed.
@ -1312,6 +1348,8 @@ When you use a ``FileField`` on a form, you must also remember to
* Normalizes to: A Python integer or long integer. * Normalizes to: A Python integer or long integer.
* Validates that the given value is an integer. Leading and trailing * Validates that the given value is an integer. Leading and trailing
whitespace is allowed, as in Python's ``int()`` function. whitespace is allowed, as in Python's ``int()`` function.
* Error message keys: ``required``, ``invalid``, ``max_value``,
``min_value``
Takes two optional arguments for validation, ``max_value`` and ``min_value``. Takes two optional arguments for validation, ``max_value`` and ``min_value``.
These control the range of values permitted in the field. These control the range of values permitted in the field.
@ -1324,6 +1362,7 @@ These control the range of values permitted in the field.
* Normalizes to: A Unicode object. * Normalizes to: A Unicode object.
* Validates that the given value is a valid IPv4 address, using a regular * Validates that the given value is a valid IPv4 address, using a regular
expression. expression.
* Error message keys: ``required``, ``invalid``
``MultipleChoiceField`` ``MultipleChoiceField``
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
@ -1333,6 +1372,7 @@ These control the range of values permitted in the field.
* Normalizes to: A list of Unicode objects. * Normalizes to: A list of Unicode objects.
* Validates that every value in the given list of values exists in the list * Validates that every value in the given list of values exists in the list
of choices. of choices.
* Error message keys: ``required``, ``invalid_choice``, ``invalid_list``
Takes one extra argument, ``choices``, which is an iterable (e.g., a list or Takes one extra argument, ``choices``, which is an iterable (e.g., a list or
tuple) of 2-tuples to use as choices for this field. tuple) of 2-tuples to use as choices for this field.
@ -1353,6 +1393,7 @@ tuple) of 2-tuples to use as choices for this field.
* Normalizes to: A Unicode object. * Normalizes to: A Unicode object.
* Validates that the given value matches against a certain regular * Validates that the given value matches against a certain regular
expression. expression.
* Error message keys: ``required``, ``invalid``
Takes one required argument, ``regex``, which is a regular expression specified Takes one required argument, ``regex``, which is a regular expression specified
either as a string or a compiled regular expression object. either as a string or a compiled regular expression object.
@ -1364,11 +1405,13 @@ Also takes the following optional arguments:
====================== ===================================================== ====================== =====================================================
``max_length`` Ensures the string has at most this many characters. ``max_length`` Ensures the string has at most this many characters.
``min_length`` Ensures the string has at least this many characters. ``min_length`` Ensures the string has at least this many characters.
``error_message`` Error message to return for failed validation. If no
message is provided, a generic error message will be
used.
====================== ===================================================== ====================== =====================================================
The optional argument ``error_message`` is also accepted for backwards
compatibility. The preferred way to provide an error message is to use the
``error_messages`` argument, passing a dictionary with ``'invalid'`` as a key
and the error message as the value.
``TimeField`` ``TimeField``
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@ -1377,6 +1420,7 @@ Also takes the following optional arguments:
* Normalizes to: A Python ``datetime.time`` object. * Normalizes to: A Python ``datetime.time`` object.
* Validates that the given value is either a ``datetime.time`` or string * Validates that the given value is either a ``datetime.time`` or string
formatted in a particular time format. formatted in a particular time format.
* Error message keys: ``required``, ``invalid``
Takes one optional argument, ``input_formats``, which is a list of formats used Takes one optional argument, ``input_formats``, which is a list of formats used
to attempt to convert a string to a valid ``datetime.time`` object. to attempt to convert a string to a valid ``datetime.time`` object.
@ -1393,6 +1437,7 @@ If no ``input_formats`` argument is provided, the default input formats are::
* Empty value: ``''`` (an empty string) * Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object. * Normalizes to: A Unicode object.
* Validates that the given value is a valid URL. * Validates that the given value is a valid URL.
* Error message keys: ``required``, ``invalid``, ``invalid_link``
Takes the following optional arguments: Takes the following optional arguments:

View File

@ -0,0 +1,315 @@
# -*- coding: utf-8 -*-
tests = r"""
>>> from django.newforms import *
# CharField ###################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s'
>>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s'
>>> f = CharField(min_length=5, max_length=10, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('1234')
Traceback (most recent call last):
...
ValidationError: [u'LENGTH 4, MIN LENGTH 5']
>>> f.clean('12345678901')
Traceback (most recent call last):
...
ValidationError: [u'LENGTH 11, MAX LENGTH 10']
# IntegerField ################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['min_value'] = 'MIN VALUE IS %s'
>>> e['max_value'] = 'MAX VALUE IS %s'
>>> f = IntegerField(min_value=5, max_value=10, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean('4')
Traceback (most recent call last):
...
ValidationError: [u'MIN VALUE IS 5']
>>> f.clean('11')
Traceback (most recent call last):
...
ValidationError: [u'MAX VALUE IS 10']
# FloatField ##################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['min_value'] = 'MIN VALUE IS %s'
>>> e['max_value'] = 'MAX VALUE IS %s'
>>> f = FloatField(min_value=5, max_value=10, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean('4')
Traceback (most recent call last):
...
ValidationError: [u'MIN VALUE IS 5']
>>> f.clean('11')
Traceback (most recent call last):
...
ValidationError: [u'MAX VALUE IS 10']
# DecimalField ################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['min_value'] = 'MIN VALUE IS %s'
>>> e['max_value'] = 'MAX VALUE IS %s'
>>> e['max_digits'] = 'MAX DIGITS IS %s'
>>> e['max_decimal_places'] = 'MAX DP IS %s'
>>> e['max_whole_digits'] = 'MAX DIGITS BEFORE DP IS %s'
>>> f = DecimalField(min_value=5, max_value=10, error_messages=e)
>>> f2 = DecimalField(max_digits=4, decimal_places=2, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean('4')
Traceback (most recent call last):
...
ValidationError: [u'MIN VALUE IS 5']
>>> f.clean('11')
Traceback (most recent call last):
...
ValidationError: [u'MAX VALUE IS 10']
>>> f2.clean('123.45')
Traceback (most recent call last):
...
ValidationError: [u'MAX DIGITS IS 4']
>>> f2.clean('1.234')
Traceback (most recent call last):
...
ValidationError: [u'MAX DP IS 2']
>>> f2.clean('123.4')
Traceback (most recent call last):
...
ValidationError: [u'MAX DIGITS BEFORE DP IS 2']
# DateField ###################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> f = DateField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
# TimeField ###################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> f = TimeField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
# DateTimeField ###############################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> f = DateTimeField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
# RegexField ##################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s'
>>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s'
>>> f = RegexField(r'^\d+$', min_length=5, max_length=10, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abcde')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean('1234')
Traceback (most recent call last):
...
ValidationError: [u'LENGTH 4, MIN LENGTH 5']
>>> f.clean('12345678901')
Traceback (most recent call last):
...
ValidationError: [u'LENGTH 11, MAX LENGTH 10']
# EmailField ##################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s'
>>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s'
>>> f = EmailField(min_length=8, max_length=10, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abcdefgh')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean('a@b.com')
Traceback (most recent call last):
...
ValidationError: [u'LENGTH 7, MIN LENGTH 8']
>>> f.clean('aye@bee.com')
Traceback (most recent call last):
...
ValidationError: [u'LENGTH 11, MAX LENGTH 10']
# FileField ##################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['missing'] = 'MISSING'
>>> e['empty'] = 'EMPTY FILE'
>>> f = FileField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean({})
Traceback (most recent call last):
...
ValidationError: [u'MISSING']
>>> f.clean({'filename': 'name', 'content':''})
Traceback (most recent call last):
...
ValidationError: [u'EMPTY FILE']
# URLField ##################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['invalid_link'] = 'INVALID LINK'
>>> f = URLField(verify_exists=True, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc.c')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean('http://www.jfoiwjfoi23jfoijoaijfoiwjofiwjefewl.com')
Traceback (most recent call last):
...
ValidationError: [u'INVALID LINK']
# BooleanField ################################################################
>>> e = {'required': 'REQUIRED'}
>>> f = BooleanField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
# ChoiceField #################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid_choice'] = '%(value)s IS INVALID CHOICE'
>>> f = ChoiceField(choices=[('a', 'aye')], error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('b')
Traceback (most recent call last):
...
ValidationError: [u'b IS INVALID CHOICE']
# MultipleChoiceField #########################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid_choice'] = '%(value)s IS INVALID CHOICE'
>>> e['invalid_list'] = 'NOT A LIST'
>>> f = MultipleChoiceField(choices=[('a', 'aye')], error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('b')
Traceback (most recent call last):
...
ValidationError: [u'NOT A LIST']
>>> f.clean(['b'])
Traceback (most recent call last):
...
ValidationError: [u'b IS INVALID CHOICE']
# SplitDateTimeField ##########################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid_date'] = 'INVALID DATE'
>>> e['invalid_time'] = 'INVALID TIME'
>>> f = SplitDateTimeField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean(['a', 'b'])
Traceback (most recent call last):
...
ValidationError: [u'INVALID DATE', u'INVALID TIME']
# IPAddressField ##############################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID IP ADDRESS'
>>> f = IPAddressField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('127.0.0')
Traceback (most recent call last):
...
ValidationError: [u'INVALID IP ADDRESS']
"""

View File

@ -2,6 +2,7 @@
from extra import tests as extra_tests from extra import tests as extra_tests
from fields import tests as fields_tests from fields import tests as fields_tests
from forms import tests as form_tests from forms import tests as form_tests
from error_messages import tests as custom_error_message_tests
from localflavor.ar import tests as localflavor_ar_tests from localflavor.ar import tests as localflavor_ar_tests
from localflavor.au import tests as localflavor_au_tests from localflavor.au import tests as localflavor_au_tests
from localflavor.br import tests as localflavor_br_tests from localflavor.br import tests as localflavor_br_tests
@ -29,6 +30,7 @@ __test__ = {
'extra_tests': extra_tests, 'extra_tests': extra_tests,
'fields_tests': fields_tests, 'fields_tests': fields_tests,
'form_tests': form_tests, 'form_tests': form_tests,
'custom_error_message_tests': custom_error_message_tests,
'localflavor_ar_tests': localflavor_ar_tests, 'localflavor_ar_tests': localflavor_ar_tests,
'localflavor_au_tests': localflavor_au_tests, 'localflavor_au_tests': localflavor_au_tests,
'localflavor_br_tests': localflavor_br_tests, 'localflavor_br_tests': localflavor_br_tests,

View File

@ -42,4 +42,11 @@ u''
# Can take a mixture in a list. # Can take a mixture in a list.
>>> print ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages >>> print ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages
<ul class="errorlist"><li>First error.</li><li>Not π.</li><li>Error.</li></ul> <ul class="errorlist"><li>First error.</li><li>Not π.</li><li>Error.</li></ul>
>>> class VeryBadError:
... def __unicode__(self): return u"A very bad error."
# Can take a non-string.
>>> print ValidationError(VeryBadError()).messages
<ul class="errorlist"><li>A very bad error.</li></ul>
""" """