1
0
mirror of https://github.com/django/django.git synced 2025-07-06 18:59:13 +00:00

queryset-refactor: Fixed up a few problems from the previous merge from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6693 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2007-11-18 12:07:25 +00:00
parent 3d07f94d68
commit 5d85a5147b
5 changed files with 167 additions and 109 deletions

View File

@ -49,7 +49,8 @@ class Field(object):
# 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
@ -106,7 +107,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):
@ -141,9 +142,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):
@ -173,11 +174,11 @@ 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):
@ -202,11 +203,11 @@ 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):
@ -238,20 +239,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 = (
@ -288,7 +289,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'
@ -319,7 +320,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'
@ -359,14 +360,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):
@ -375,11 +376,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):
""" """
@ -390,7 +395,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(
@ -404,8 +409,8 @@ class EmailField(RegexField):
} }
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
@ -445,11 +450,11 @@ 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):
@ -477,7 +482,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(
@ -496,7 +501,8 @@ class URLField(RegexField):
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
@ -521,9 +527,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):
@ -583,7 +589,7 @@ 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):
@ -599,17 +605,17 @@ class MultipleChoiceField(ChoiceField):
Validates that the input is a list or tuple. Validates that the input is a list or tuple.
""" """
if self.required and not value: if self.required and not value:
raise ValidationError(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):
@ -679,18 +685,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:
@ -720,7 +726,13 @@ class SplitDateTimeField(MultiValueField):
} }
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):
@ -728,9 +740,9 @@ class SplitDateTimeField(MultiValueField):
# Raise a validation error if time or date is empty # Raise a validation error if time or date is empty
# (possible if SplitDateTimeField has required=False). # (possible if SplitDateTimeField has required=False).
if data_list[0] in 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
@ -742,7 +754,4 @@ class IPAddressField(RegexField):
} }
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

@ -46,13 +46,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])
@ -62,4 +67,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

@ -1,57 +1,57 @@
from django.template import Library, Node, TemplateSyntaxError from django.template import Library, Node, TemplateSyntaxError
from django.template import resolve_variable from django.template import resolve_variable
from django.core.cache import cache from django.core.cache import cache
from django.utils.encoding import force_unicode from django.utils.encoding import force_unicode
register = Library() register = Library()
class CacheNode(Node): class CacheNode(Node):
def __init__(self, nodelist, expire_time, fragment_name, vary_on): def __init__(self, nodelist, expire_time, fragment_name, vary_on):
self.nodelist = nodelist self.nodelist = nodelist
self.expire_time = expire_time self.expire_time = expire_time
self.fragment_name = fragment_name self.fragment_name = fragment_name
self.vary_on = vary_on self.vary_on = vary_on
def render(self, context): def render(self, context):
# Build a unicode key for this fragment and all vary-on's. # Build a unicode key for this fragment and all vary-on's.
cache_key = u':'.join([self.fragment_name] + \ cache_key = u':'.join([self.fragment_name] + \
[force_unicode(resolve_variable(var, context)) for var in self.vary_on]) [force_unicode(resolve_variable(var, context)) for var in self.vary_on])
value = cache.get(cache_key) value = cache.get(cache_key)
if value is None: if value is None:
value = self.nodelist.render(context) value = self.nodelist.render(context)
cache.set(cache_key, value, self.expire_time) cache.set(cache_key, value, self.expire_time)
return value return value
def do_cache(parser, token): def do_cache(parser, token):
""" """
This will cache the contents of a template fragment for a given amount This will cache the contents of a template fragment for a given amount
of time. of time.
Usage:: Usage::
{% load cache %} {% load cache %}
{% cache [expire_time] [fragment_name] %} {% cache [expire_time] [fragment_name] %}
.. some expensive processing .. .. some expensive processing ..
{% endcache %} {% endcache %}
This tag also supports varying by a list of arguments:: This tag also supports varying by a list of arguments::
{% load cache %} {% load cache %}
{% cache [expire_time] [fragment_name] [var1] [var2] .. %} {% cache [expire_time] [fragment_name] [var1] [var2] .. %}
.. some expensive processing .. .. some expensive processing ..
{% endcache %} {% endcache %}
Each unique set of arguments will result in a unique cache entry. Each unique set of arguments will result in a unique cache entry.
""" """
nodelist = parser.parse(('endcache',)) nodelist = parser.parse(('endcache',))
parser.delete_first_token() parser.delete_first_token()
tokens = token.contents.split() tokens = token.contents.split()
if len(tokens) < 3: if len(tokens) < 3:
raise TemplateSyntaxError(u"'%r' tag requires at least 2 arguments." % tokens[0]) raise TemplateSyntaxError(u"'%r' tag requires at least 2 arguments." % tokens[0])
try: try:
expire_time = int(tokens[1]) expire_time = int(tokens[1])
except ValueError: except ValueError:
raise TemplateSyntaxError(u"First argument to '%r' must be an integer (got '%s')." % (tokens[0], tokens[1])) raise TemplateSyntaxError(u"First argument to '%r' must be an integer (got '%s')." % (tokens[0], tokens[1]))
return CacheNode(nodelist, expire_time, tokens[2], tokens[3:]) return CacheNode(nodelist, expire_time, tokens[2], tokens[3:])
register.tag('cache', do_cache) register.tag('cache', do_cache)

View File

@ -150,7 +150,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: