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:
parent
ee49e934d9
commit
26ea06b0ab
|
@ -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)
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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']
|
||||||
|
"""
|
|
@ -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,
|
||||||
|
|
|
@ -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>
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue