web/lib/django/forms/fields.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     1 """
       
     2 Field classes.
       
     3 """
       
     4 
       
     5 import datetime
       
     6 import os
       
     7 import re
       
     8 import time
       
     9 import urlparse
       
    10 import warnings
       
    11 from decimal import Decimal, DecimalException
       
    12 try:
       
    13     from cStringIO import StringIO
       
    14 except ImportError:
       
    15     from StringIO import StringIO
       
    16 
       
    17 from django.core.exceptions import ValidationError
       
    18 from django.core import validators
       
    19 import django.utils.copycompat as copy
       
    20 from django.utils import formats
       
    21 from django.utils.translation import ugettext_lazy as _
       
    22 from django.utils.encoding import smart_unicode, smart_str
       
    23 from django.utils.functional import lazy
       
    24 
       
    25 # Provide this import for backwards compatibility.
       
    26 from django.core.validators import EMPTY_VALUES
       
    27 
       
    28 from util import ErrorList
       
    29 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, \
       
    30         FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, \
       
    31         DateInput, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget
       
    32 
       
    33 __all__ = (
       
    34     'Field', 'CharField', 'IntegerField',
       
    35     'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
       
    36     'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
       
    37     'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField', 'TimeField',
       
    38     'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField',
       
    39     'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
       
    40     'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
       
    41     'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField',
       
    42     'TypedChoiceField'
       
    43 )
       
    44 
       
    45 def en_format(name):
       
    46     """
       
    47     Helper function to stay backward compatible.
       
    48     """
       
    49     from django.conf.locale.en import formats
       
    50     warnings.warn(
       
    51         "`django.forms.fields.DEFAULT_%s` is deprecated; use `django.utils.formats.get_format('%s')` instead." % (name, name),
       
    52         PendingDeprecationWarning
       
    53     )
       
    54     return getattr(formats, name)
       
    55 
       
    56 DEFAULT_DATE_INPUT_FORMATS = lazy(lambda: en_format('DATE_INPUT_FORMATS'), tuple, list)()
       
    57 DEFAULT_TIME_INPUT_FORMATS = lazy(lambda: en_format('TIME_INPUT_FORMATS'), tuple, list)()
       
    58 DEFAULT_DATETIME_INPUT_FORMATS = lazy(lambda: en_format('DATETIME_INPUT_FORMATS'), tuple, list)()
       
    59 
       
    60 class Field(object):
       
    61     widget = TextInput # Default widget to use when rendering this type of Field.
       
    62     hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
       
    63     default_validators = [] # Default set of validators
       
    64     default_error_messages = {
       
    65         'required': _(u'This field is required.'),
       
    66         'invalid': _(u'Enter a valid value.'),
       
    67     }
       
    68 
       
    69     # Tracks each time a Field instance is created. Used to retain order.
       
    70     creation_counter = 0
       
    71 
       
    72     def __init__(self, required=True, widget=None, label=None, initial=None,
       
    73                  help_text=None, error_messages=None, show_hidden_initial=False,
       
    74                  validators=[], localize=False):
       
    75         # required -- Boolean that specifies whether the field is required.
       
    76         #             True by default.
       
    77         # widget -- A Widget class, or instance of a Widget class, that should
       
    78         #           be used for this Field when displaying it. Each Field has a
       
    79         #           default Widget that it'll use if you don't specify this. In
       
    80         #           most cases, the default widget is TextInput.
       
    81         # label -- A verbose name for this field, for use in displaying this
       
    82         #          field in a form. By default, Django will use a "pretty"
       
    83         #          version of the form field name, if the Field is part of a
       
    84         #          Form.
       
    85         # initial -- A value to use in this Field's initial display. This value
       
    86         #            is *not* used as a fallback if data isn't given.
       
    87         # help_text -- An optional string to use as "help text" for this Field.
       
    88         # error_messages -- An optional dictionary to override the default
       
    89         #                   messages that the field will raise.
       
    90         # show_hidden_initial -- Boolean that specifies if it is needed to render a
       
    91         #                        hidden widget with initial value after widget.
       
    92         # validators -- List of addtional validators to use
       
    93         # localize -- Boolean that specifies if the field should be localized.
       
    94         if label is not None:
       
    95             label = smart_unicode(label)
       
    96         self.required, self.label, self.initial = required, label, initial
       
    97         self.show_hidden_initial = show_hidden_initial
       
    98         if help_text is None:
       
    99             self.help_text = u''
       
   100         else:
       
   101             self.help_text = smart_unicode(help_text)
       
   102         widget = widget or self.widget
       
   103         if isinstance(widget, type):
       
   104             widget = widget()
       
   105 
       
   106         # Trigger the localization machinery if needed.
       
   107         self.localize = localize
       
   108         if self.localize:
       
   109             widget.is_localized = True
       
   110 
       
   111         # Hook into self.widget_attrs() for any Field-specific HTML attributes.
       
   112         extra_attrs = self.widget_attrs(widget)
       
   113         if extra_attrs:
       
   114             widget.attrs.update(extra_attrs)
       
   115 
       
   116         self.widget = widget
       
   117 
       
   118         # Increase the creation counter, and save our local copy.
       
   119         self.creation_counter = Field.creation_counter
       
   120         Field.creation_counter += 1
       
   121 
       
   122         messages = {}
       
   123         for c in reversed(self.__class__.__mro__):
       
   124             messages.update(getattr(c, 'default_error_messages', {}))
       
   125         messages.update(error_messages or {})
       
   126         self.error_messages = messages
       
   127 
       
   128         self.validators = self.default_validators + validators
       
   129 
       
   130     def to_python(self, value):
       
   131         return value
       
   132 
       
   133     def validate(self, value):
       
   134         if value in validators.EMPTY_VALUES and self.required:
       
   135             raise ValidationError(self.error_messages['required'])
       
   136 
       
   137     def run_validators(self, value):
       
   138         if value in validators.EMPTY_VALUES:
       
   139             return
       
   140         errors = []
       
   141         for v in self.validators:
       
   142             try:
       
   143                 v(value)
       
   144             except ValidationError, e:
       
   145                 if hasattr(e, 'code') and e.code in self.error_messages:
       
   146                     message = self.error_messages[e.code]
       
   147                     if e.params:
       
   148                         message = message % e.params
       
   149                     errors.append(message)
       
   150                 else:
       
   151                     errors.extend(e.messages)
       
   152         if errors:
       
   153             raise ValidationError(errors)
       
   154 
       
   155     def clean(self, value):
       
   156         """
       
   157         Validates the given value and returns its "cleaned" value as an
       
   158         appropriate Python object.
       
   159 
       
   160         Raises ValidationError for any errors.
       
   161         """
       
   162         value = self.to_python(value)
       
   163         self.validate(value)
       
   164         self.run_validators(value)
       
   165         return value
       
   166 
       
   167     def widget_attrs(self, widget):
       
   168         """
       
   169         Given a Widget instance (*not* a Widget class), returns a dictionary of
       
   170         any HTML attributes that should be added to the Widget, based on this
       
   171         Field.
       
   172         """
       
   173         return {}
       
   174 
       
   175     def __deepcopy__(self, memo):
       
   176         result = copy.copy(self)
       
   177         memo[id(self)] = result
       
   178         result.widget = copy.deepcopy(self.widget, memo)
       
   179         return result
       
   180 
       
   181 class CharField(Field):
       
   182     def __init__(self, max_length=None, min_length=None, *args, **kwargs):
       
   183         self.max_length, self.min_length = max_length, min_length
       
   184         super(CharField, self).__init__(*args, **kwargs)
       
   185         if min_length is not None:
       
   186             self.validators.append(validators.MinLengthValidator(min_length))
       
   187         if max_length is not None:
       
   188             self.validators.append(validators.MaxLengthValidator(max_length))
       
   189 
       
   190     def to_python(self, value):
       
   191         "Returns a Unicode object."
       
   192         if value in validators.EMPTY_VALUES:
       
   193             return u''
       
   194         return smart_unicode(value)
       
   195 
       
   196     def widget_attrs(self, widget):
       
   197         if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
       
   198             # The HTML attribute is maxlength, not max_length.
       
   199             return {'maxlength': str(self.max_length)}
       
   200 
       
   201 class IntegerField(Field):
       
   202     default_error_messages = {
       
   203         'invalid': _(u'Enter a whole number.'),
       
   204         'max_value': _(u'Ensure this value is less than or equal to %(limit_value)s.'),
       
   205         'min_value': _(u'Ensure this value is greater than or equal to %(limit_value)s.'),
       
   206     }
       
   207 
       
   208     def __init__(self, max_value=None, min_value=None, *args, **kwargs):
       
   209         super(IntegerField, self).__init__(*args, **kwargs)
       
   210 
       
   211         if max_value is not None:
       
   212             self.validators.append(validators.MaxValueValidator(max_value))
       
   213         if min_value is not None:
       
   214             self.validators.append(validators.MinValueValidator(min_value))
       
   215 
       
   216     def to_python(self, value):
       
   217         """
       
   218         Validates that int() can be called on the input. Returns the result
       
   219         of int(). Returns None for empty values.
       
   220         """
       
   221         value = super(IntegerField, self).to_python(value)
       
   222         if value in validators.EMPTY_VALUES:
       
   223             return None
       
   224         if self.localize:
       
   225             value = formats.sanitize_separators(value)
       
   226         try:
       
   227             value = int(str(value))
       
   228         except (ValueError, TypeError):
       
   229             raise ValidationError(self.error_messages['invalid'])
       
   230         return value
       
   231 
       
   232 class FloatField(IntegerField):
       
   233     default_error_messages = {
       
   234         'invalid': _(u'Enter a number.'),
       
   235     }
       
   236 
       
   237     def to_python(self, value):
       
   238         """
       
   239         Validates that float() can be called on the input. Returns the result
       
   240         of float(). Returns None for empty values.
       
   241         """
       
   242         value = super(IntegerField, self).to_python(value)
       
   243         if value in validators.EMPTY_VALUES:
       
   244             return None
       
   245         if self.localize:
       
   246             value = formats.sanitize_separators(value)
       
   247         try:
       
   248             value = float(value)
       
   249         except (ValueError, TypeError):
       
   250             raise ValidationError(self.error_messages['invalid'])
       
   251         return value
       
   252 
       
   253 class DecimalField(Field):
       
   254     default_error_messages = {
       
   255         'invalid': _(u'Enter a number.'),
       
   256         'max_value': _(u'Ensure this value is less than or equal to %(limit_value)s.'),
       
   257         'min_value': _(u'Ensure this value is greater than or equal to %(limit_value)s.'),
       
   258         'max_digits': _('Ensure that there are no more than %s digits in total.'),
       
   259         'max_decimal_places': _('Ensure that there are no more than %s decimal places.'),
       
   260         'max_whole_digits': _('Ensure that there are no more than %s digits before the decimal point.')
       
   261     }
       
   262 
       
   263     def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs):
       
   264         self.max_digits, self.decimal_places = max_digits, decimal_places
       
   265         Field.__init__(self, *args, **kwargs)
       
   266 
       
   267         if max_value is not None:
       
   268             self.validators.append(validators.MaxValueValidator(max_value))
       
   269         if min_value is not None:
       
   270             self.validators.append(validators.MinValueValidator(min_value))
       
   271 
       
   272     def to_python(self, value):
       
   273         """
       
   274         Validates that the input is a decimal number. Returns a Decimal
       
   275         instance. Returns None for empty values. Ensures that there are no more
       
   276         than max_digits in the number, and no more than decimal_places digits
       
   277         after the decimal point.
       
   278         """
       
   279         if value in validators.EMPTY_VALUES:
       
   280             return None
       
   281         if self.localize:
       
   282             value = formats.sanitize_separators(value)
       
   283         value = smart_str(value).strip()
       
   284         try:
       
   285             value = Decimal(value)
       
   286         except DecimalException:
       
   287             raise ValidationError(self.error_messages['invalid'])
       
   288         return value
       
   289 
       
   290     def validate(self, value):
       
   291         super(DecimalField, self).validate(value)
       
   292         if value in validators.EMPTY_VALUES:
       
   293             return
       
   294         # Check for NaN, Inf and -Inf values. We can't compare directly for NaN,
       
   295         # since it is never equal to itself. However, NaN is the only value that
       
   296         # isn't equal to itself, so we can use this to identify NaN
       
   297         if value != value or value == Decimal("Inf") or value == Decimal("-Inf"):
       
   298             raise ValidationError(self.error_messages['invalid'])
       
   299         sign, digittuple, exponent = value.as_tuple()
       
   300         decimals = abs(exponent)
       
   301         # digittuple doesn't include any leading zeros.
       
   302         digits = len(digittuple)
       
   303         if decimals > digits:
       
   304             # We have leading zeros up to or past the decimal point.  Count
       
   305             # everything past the decimal point as a digit.  We do not count
       
   306             # 0 before the decimal point as a digit since that would mean
       
   307             # we would not allow max_digits = decimal_places.
       
   308             digits = decimals
       
   309         whole_digits = digits - decimals
       
   310 
       
   311         if self.max_digits is not None and digits > self.max_digits:
       
   312             raise ValidationError(self.error_messages['max_digits'] % self.max_digits)
       
   313         if self.decimal_places is not None and decimals > self.decimal_places:
       
   314             raise ValidationError(self.error_messages['max_decimal_places'] % self.decimal_places)
       
   315         if self.max_digits is not None and self.decimal_places is not None and whole_digits > (self.max_digits - self.decimal_places):
       
   316             raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
       
   317         return value
       
   318 
       
   319 class DateField(Field):
       
   320     widget = DateInput
       
   321     default_error_messages = {
       
   322         'invalid': _(u'Enter a valid date.'),
       
   323     }
       
   324 
       
   325     def __init__(self, input_formats=None, *args, **kwargs):
       
   326         super(DateField, self).__init__(*args, **kwargs)
       
   327         self.input_formats = input_formats
       
   328 
       
   329     def to_python(self, value):
       
   330         """
       
   331         Validates that the input can be converted to a date. Returns a Python
       
   332         datetime.date object.
       
   333         """
       
   334         if value in validators.EMPTY_VALUES:
       
   335             return None
       
   336         if isinstance(value, datetime.datetime):
       
   337             return value.date()
       
   338         if isinstance(value, datetime.date):
       
   339             return value
       
   340         for format in self.input_formats or formats.get_format('DATE_INPUT_FORMATS'):
       
   341             try:
       
   342                 return datetime.date(*time.strptime(value, format)[:3])
       
   343             except ValueError:
       
   344                 continue
       
   345         raise ValidationError(self.error_messages['invalid'])
       
   346 
       
   347 class TimeField(Field):
       
   348     widget = TimeInput
       
   349     default_error_messages = {
       
   350         'invalid': _(u'Enter a valid time.')
       
   351     }
       
   352 
       
   353     def __init__(self, input_formats=None, *args, **kwargs):
       
   354         super(TimeField, self).__init__(*args, **kwargs)
       
   355         self.input_formats = input_formats
       
   356 
       
   357     def to_python(self, value):
       
   358         """
       
   359         Validates that the input can be converted to a time. Returns a Python
       
   360         datetime.time object.
       
   361         """
       
   362         if value in validators.EMPTY_VALUES:
       
   363             return None
       
   364         if isinstance(value, datetime.time):
       
   365             return value
       
   366         for format in self.input_formats or formats.get_format('TIME_INPUT_FORMATS'):
       
   367             try:
       
   368                 return datetime.time(*time.strptime(value, format)[3:6])
       
   369             except ValueError:
       
   370                 continue
       
   371         raise ValidationError(self.error_messages['invalid'])
       
   372 
       
   373 class DateTimeField(Field):
       
   374     widget = DateTimeInput
       
   375     default_error_messages = {
       
   376         'invalid': _(u'Enter a valid date/time.'),
       
   377     }
       
   378 
       
   379     def __init__(self, input_formats=None, *args, **kwargs):
       
   380         super(DateTimeField, self).__init__(*args, **kwargs)
       
   381         self.input_formats = input_formats
       
   382 
       
   383     def to_python(self, value):
       
   384         """
       
   385         Validates that the input can be converted to a datetime. Returns a
       
   386         Python datetime.datetime object.
       
   387         """
       
   388         if value in validators.EMPTY_VALUES:
       
   389             return None
       
   390         if isinstance(value, datetime.datetime):
       
   391             return value
       
   392         if isinstance(value, datetime.date):
       
   393             return datetime.datetime(value.year, value.month, value.day)
       
   394         if isinstance(value, list):
       
   395             # Input comes from a SplitDateTimeWidget, for example. So, it's two
       
   396             # components: date and time.
       
   397             if len(value) != 2:
       
   398                 raise ValidationError(self.error_messages['invalid'])
       
   399             value = '%s %s' % tuple(value)
       
   400         for format in self.input_formats or formats.get_format('DATETIME_INPUT_FORMATS'):
       
   401             try:
       
   402                 return datetime.datetime(*time.strptime(value, format)[:6])
       
   403             except ValueError:
       
   404                 continue
       
   405         raise ValidationError(self.error_messages['invalid'])
       
   406 
       
   407 class RegexField(CharField):
       
   408     def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
       
   409         """
       
   410         regex can be either a string or a compiled regular expression object.
       
   411         error_message is an optional error message to use, if
       
   412         'Enter a valid value' is too generic for you.
       
   413         """
       
   414         # error_message is just kept for backwards compatibility:
       
   415         if error_message:
       
   416             error_messages = kwargs.get('error_messages') or {}
       
   417             error_messages['invalid'] = error_message
       
   418             kwargs['error_messages'] = error_messages
       
   419         super(RegexField, self).__init__(max_length, min_length, *args, **kwargs)
       
   420         if isinstance(regex, basestring):
       
   421             regex = re.compile(regex)
       
   422         self.regex = regex
       
   423         self.validators.append(validators.RegexValidator(regex=regex))
       
   424 
       
   425 class EmailField(CharField):
       
   426     default_error_messages = {
       
   427         'invalid': _(u'Enter a valid e-mail address.'),
       
   428     }
       
   429     default_validators = [validators.validate_email]
       
   430 
       
   431 class FileField(Field):
       
   432     widget = FileInput
       
   433     default_error_messages = {
       
   434         'invalid': _(u"No file was submitted. Check the encoding type on the form."),
       
   435         'missing': _(u"No file was submitted."),
       
   436         'empty': _(u"The submitted file is empty."),
       
   437         'max_length': _(u'Ensure this filename has at most %(max)d characters (it has %(length)d).'),
       
   438     }
       
   439 
       
   440     def __init__(self, *args, **kwargs):
       
   441         self.max_length = kwargs.pop('max_length', None)
       
   442         super(FileField, self).__init__(*args, **kwargs)
       
   443 
       
   444     def to_python(self, data):
       
   445         if data in validators.EMPTY_VALUES:
       
   446             return None
       
   447 
       
   448         # UploadedFile objects should have name and size attributes.
       
   449         try:
       
   450             file_name = data.name
       
   451             file_size = data.size
       
   452         except AttributeError:
       
   453             raise ValidationError(self.error_messages['invalid'])
       
   454 
       
   455         if self.max_length is not None and len(file_name) > self.max_length:
       
   456             error_values =  {'max': self.max_length, 'length': len(file_name)}
       
   457             raise ValidationError(self.error_messages['max_length'] % error_values)
       
   458         if not file_name:
       
   459             raise ValidationError(self.error_messages['invalid'])
       
   460         if not file_size:
       
   461             raise ValidationError(self.error_messages['empty'])
       
   462 
       
   463         return data
       
   464 
       
   465     def clean(self, data, initial=None):
       
   466         if not data and initial:
       
   467             return initial
       
   468         return super(FileField, self).clean(data)
       
   469 
       
   470 class ImageField(FileField):
       
   471     default_error_messages = {
       
   472         'invalid_image': _(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."),
       
   473     }
       
   474 
       
   475     def to_python(self, data):
       
   476         """
       
   477         Checks that the file-upload field data contains a valid image (GIF, JPG,
       
   478         PNG, possibly others -- whatever the Python Imaging Library supports).
       
   479         """
       
   480         f = super(ImageField, self).to_python(data)
       
   481         if f is None:
       
   482             return None
       
   483 
       
   484         # Try to import PIL in either of the two ways it can end up installed.
       
   485         try:
       
   486             from PIL import Image
       
   487         except ImportError:
       
   488             import Image
       
   489 
       
   490         # We need to get a file object for PIL. We might have a path or we might
       
   491         # have to read the data into memory.
       
   492         if hasattr(data, 'temporary_file_path'):
       
   493             file = data.temporary_file_path()
       
   494         else:
       
   495             if hasattr(data, 'read'):
       
   496                 file = StringIO(data.read())
       
   497             else:
       
   498                 file = StringIO(data['content'])
       
   499 
       
   500         try:
       
   501             # load() is the only method that can spot a truncated JPEG,
       
   502             #  but it cannot be called sanely after verify()
       
   503             trial_image = Image.open(file)
       
   504             trial_image.load()
       
   505 
       
   506             # Since we're about to use the file again we have to reset the
       
   507             # file object if possible.
       
   508             if hasattr(file, 'reset'):
       
   509                 file.reset()
       
   510 
       
   511             # verify() is the only method that can spot a corrupt PNG,
       
   512             #  but it must be called immediately after the constructor
       
   513             trial_image = Image.open(file)
       
   514             trial_image.verify()
       
   515         except ImportError:
       
   516             # Under PyPy, it is possible to import PIL. However, the underlying
       
   517             # _imaging C module isn't available, so an ImportError will be
       
   518             # raised. Catch and re-raise.
       
   519             raise
       
   520         except Exception: # Python Imaging Library doesn't recognize it as an image
       
   521             raise ValidationError(self.error_messages['invalid_image'])
       
   522         if hasattr(f, 'seek') and callable(f.seek):
       
   523             f.seek(0)
       
   524         return f
       
   525 
       
   526 class URLField(CharField):
       
   527     default_error_messages = {
       
   528         'invalid': _(u'Enter a valid URL.'),
       
   529         'invalid_link': _(u'This URL appears to be a broken link.'),
       
   530     }
       
   531 
       
   532     def __init__(self, max_length=None, min_length=None, verify_exists=False,
       
   533             validator_user_agent=validators.URL_VALIDATOR_USER_AGENT, *args, **kwargs):
       
   534         super(URLField, self).__init__(max_length, min_length, *args,
       
   535                                        **kwargs)
       
   536         self.validators.append(validators.URLValidator(verify_exists=verify_exists, validator_user_agent=validator_user_agent))
       
   537 
       
   538     def to_python(self, value):
       
   539         if value:
       
   540             if '://' not in value:
       
   541                 # If no URL scheme given, assume http://
       
   542                 value = u'http://%s' % value
       
   543             url_fields = list(urlparse.urlsplit(value))
       
   544             if not url_fields[2]:
       
   545                 # the path portion may need to be added before query params
       
   546                 url_fields[2] = '/'
       
   547                 value = urlparse.urlunsplit(url_fields)
       
   548         return super(URLField, self).to_python(value)
       
   549 
       
   550 class BooleanField(Field):
       
   551     widget = CheckboxInput
       
   552 
       
   553     def to_python(self, value):
       
   554         """Returns a Python boolean object."""
       
   555         # Explicitly check for the string 'False', which is what a hidden field
       
   556         # will submit for False. Also check for '0', since this is what
       
   557         # RadioSelect will provide. Because bool("True") == bool('1') == True,
       
   558         # we don't need to handle that explicitly.
       
   559         if value in ('False', '0'):
       
   560             value = False
       
   561         else:
       
   562             value = bool(value)
       
   563         value = super(BooleanField, self).to_python(value)
       
   564         if not value and self.required:
       
   565             raise ValidationError(self.error_messages['required'])
       
   566         return value
       
   567 
       
   568 class NullBooleanField(BooleanField):
       
   569     """
       
   570     A field whose valid values are None, True and False. Invalid values are
       
   571     cleaned to None.
       
   572     """
       
   573     widget = NullBooleanSelect
       
   574 
       
   575     def to_python(self, value):
       
   576         """
       
   577         Explicitly checks for the string 'True' and 'False', which is what a
       
   578         hidden field will submit for True and False, and for '1' and '0', which
       
   579         is what a RadioField will submit. Unlike the Booleanfield we need to
       
   580         explicitly check for True, because we are not using the bool() function
       
   581         """
       
   582         if value in (True, 'True', '1'):
       
   583             return True
       
   584         elif value in (False, 'False', '0'):
       
   585             return False
       
   586         else:
       
   587             return None
       
   588 
       
   589     def validate(self, value):
       
   590         pass
       
   591 
       
   592 class ChoiceField(Field):
       
   593     widget = Select
       
   594     default_error_messages = {
       
   595         'invalid_choice': _(u'Select a valid choice. %(value)s is not one of the available choices.'),
       
   596     }
       
   597 
       
   598     def __init__(self, choices=(), required=True, widget=None, label=None,
       
   599                  initial=None, help_text=None, *args, **kwargs):
       
   600         super(ChoiceField, self).__init__(required=required, widget=widget, label=label,
       
   601                                         initial=initial, help_text=help_text, *args, **kwargs)
       
   602         self.choices = choices
       
   603 
       
   604     def _get_choices(self):
       
   605         return self._choices
       
   606 
       
   607     def _set_choices(self, value):
       
   608         # Setting choices also sets the choices on the widget.
       
   609         # choices can be any iterable, but we call list() on it because
       
   610         # it will be consumed more than once.
       
   611         self._choices = self.widget.choices = list(value)
       
   612 
       
   613     choices = property(_get_choices, _set_choices)
       
   614 
       
   615     def to_python(self, value):
       
   616         "Returns a Unicode object."
       
   617         if value in validators.EMPTY_VALUES:
       
   618             return u''
       
   619         return smart_unicode(value)
       
   620 
       
   621     def validate(self, value):
       
   622         """
       
   623         Validates that the input is in self.choices.
       
   624         """
       
   625         super(ChoiceField, self).validate(value)
       
   626         if value and not self.valid_value(value):
       
   627             raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
       
   628 
       
   629     def valid_value(self, value):
       
   630         "Check to see if the provided value is a valid choice"
       
   631         for k, v in self.choices:
       
   632             if isinstance(v, (list, tuple)):
       
   633                 # This is an optgroup, so look inside the group for options
       
   634                 for k2, v2 in v:
       
   635                     if value == smart_unicode(k2):
       
   636                         return True
       
   637             else:
       
   638                 if value == smart_unicode(k):
       
   639                     return True
       
   640         return False
       
   641 
       
   642 class TypedChoiceField(ChoiceField):
       
   643     def __init__(self, *args, **kwargs):
       
   644         self.coerce = kwargs.pop('coerce', lambda val: val)
       
   645         self.empty_value = kwargs.pop('empty_value', '')
       
   646         super(TypedChoiceField, self).__init__(*args, **kwargs)
       
   647 
       
   648     def to_python(self, value):
       
   649         """
       
   650         Validate that the value is in self.choices and can be coerced to the
       
   651         right type.
       
   652         """
       
   653         value = super(TypedChoiceField, self).to_python(value)
       
   654         super(TypedChoiceField, self).validate(value)
       
   655         if value == self.empty_value or value in validators.EMPTY_VALUES:
       
   656             return self.empty_value
       
   657         try:
       
   658             value = self.coerce(value)
       
   659         except (ValueError, TypeError, ValidationError):
       
   660             raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
       
   661         return value
       
   662 
       
   663     def validate(self, value):
       
   664         pass
       
   665 
       
   666 class MultipleChoiceField(ChoiceField):
       
   667     hidden_widget = MultipleHiddenInput
       
   668     widget = SelectMultiple
       
   669     default_error_messages = {
       
   670         'invalid_choice': _(u'Select a valid choice. %(value)s is not one of the available choices.'),
       
   671         'invalid_list': _(u'Enter a list of values.'),
       
   672     }
       
   673 
       
   674     def to_python(self, value):
       
   675         if not value:
       
   676             return []
       
   677         elif not isinstance(value, (list, tuple)):
       
   678             raise ValidationError(self.error_messages['invalid_list'])
       
   679         return [smart_unicode(val) for val in value]
       
   680 
       
   681     def validate(self, value):
       
   682         """
       
   683         Validates that the input is a list or tuple.
       
   684         """
       
   685         if self.required and not value:
       
   686             raise ValidationError(self.error_messages['required'])
       
   687         # Validate that each value in the value list is in self.choices.
       
   688         for val in value:
       
   689             if not self.valid_value(val):
       
   690                 raise ValidationError(self.error_messages['invalid_choice'] % {'value': val})
       
   691 
       
   692 class ComboField(Field):
       
   693     """
       
   694     A Field whose clean() method calls multiple Field clean() methods.
       
   695     """
       
   696     def __init__(self, fields=(), *args, **kwargs):
       
   697         super(ComboField, self).__init__(*args, **kwargs)
       
   698         # Set 'required' to False on the individual fields, because the
       
   699         # required validation will be handled by ComboField, not by those
       
   700         # individual fields.
       
   701         for f in fields:
       
   702             f.required = False
       
   703         self.fields = fields
       
   704 
       
   705     def clean(self, value):
       
   706         """
       
   707         Validates the given value against all of self.fields, which is a
       
   708         list of Field instances.
       
   709         """
       
   710         super(ComboField, self).clean(value)
       
   711         for field in self.fields:
       
   712             value = field.clean(value)
       
   713         return value
       
   714 
       
   715 class MultiValueField(Field):
       
   716     """
       
   717     A Field that aggregates the logic of multiple Fields.
       
   718 
       
   719     Its clean() method takes a "decompressed" list of values, which are then
       
   720     cleaned into a single value according to self.fields. Each value in
       
   721     this list is cleaned by the corresponding field -- the first value is
       
   722     cleaned by the first field, the second value is cleaned by the second
       
   723     field, etc. Once all fields are cleaned, the list of clean values is
       
   724     "compressed" into a single value.
       
   725 
       
   726     Subclasses should not have to implement clean(). Instead, they must
       
   727     implement compress(), which takes a list of valid values and returns a
       
   728     "compressed" version of those values -- a single value.
       
   729 
       
   730     You'll probably want to use this with MultiWidget.
       
   731     """
       
   732     default_error_messages = {
       
   733         'invalid': _(u'Enter a list of values.'),
       
   734     }
       
   735 
       
   736     def __init__(self, fields=(), *args, **kwargs):
       
   737         super(MultiValueField, self).__init__(*args, **kwargs)
       
   738         # Set 'required' to False on the individual fields, because the
       
   739         # required validation will be handled by MultiValueField, not by those
       
   740         # individual fields.
       
   741         for f in fields:
       
   742             f.required = False
       
   743         self.fields = fields
       
   744 
       
   745     def validate(self, value):
       
   746         pass
       
   747 
       
   748     def clean(self, value):
       
   749         """
       
   750         Validates every value in the given list. A value is validated against
       
   751         the corresponding Field in self.fields.
       
   752 
       
   753         For example, if this MultiValueField was instantiated with
       
   754         fields=(DateField(), TimeField()), clean() would call
       
   755         DateField.clean(value[0]) and TimeField.clean(value[1]).
       
   756         """
       
   757         clean_data = []
       
   758         errors = ErrorList()
       
   759         if not value or isinstance(value, (list, tuple)):
       
   760             if not value or not [v for v in value if v not in validators.EMPTY_VALUES]:
       
   761                 if self.required:
       
   762                     raise ValidationError(self.error_messages['required'])
       
   763                 else:
       
   764                     return self.compress([])
       
   765         else:
       
   766             raise ValidationError(self.error_messages['invalid'])
       
   767         for i, field in enumerate(self.fields):
       
   768             try:
       
   769                 field_value = value[i]
       
   770             except IndexError:
       
   771                 field_value = None
       
   772             if self.required and field_value in validators.EMPTY_VALUES:
       
   773                 raise ValidationError(self.error_messages['required'])
       
   774             try:
       
   775                 clean_data.append(field.clean(field_value))
       
   776             except ValidationError, e:
       
   777                 # Collect all validation errors in a single list, which we'll
       
   778                 # raise at the end of clean(), rather than raising a single
       
   779                 # exception for the first error we encounter.
       
   780                 errors.extend(e.messages)
       
   781         if errors:
       
   782             raise ValidationError(errors)
       
   783 
       
   784         out = self.compress(clean_data)
       
   785         self.validate(out)
       
   786         return out
       
   787 
       
   788     def compress(self, data_list):
       
   789         """
       
   790         Returns a single value for the given list of values. The values can be
       
   791         assumed to be valid.
       
   792 
       
   793         For example, if this MultiValueField was instantiated with
       
   794         fields=(DateField(), TimeField()), this might return a datetime
       
   795         object created by combining the date and time in data_list.
       
   796         """
       
   797         raise NotImplementedError('Subclasses must implement this method.')
       
   798 
       
   799 class FilePathField(ChoiceField):
       
   800     def __init__(self, path, match=None, recursive=False, required=True,
       
   801                  widget=None, label=None, initial=None, help_text=None,
       
   802                  *args, **kwargs):
       
   803         self.path, self.match, self.recursive = path, match, recursive
       
   804         super(FilePathField, self).__init__(choices=(), required=required,
       
   805             widget=widget, label=label, initial=initial, help_text=help_text,
       
   806             *args, **kwargs)
       
   807 
       
   808         if self.required:
       
   809             self.choices = []
       
   810         else:
       
   811             self.choices = [("", "---------")]
       
   812 
       
   813         if self.match is not None:
       
   814             self.match_re = re.compile(self.match)
       
   815 
       
   816         if recursive:
       
   817             for root, dirs, files in os.walk(self.path):
       
   818                 for f in files:
       
   819                     if self.match is None or self.match_re.search(f):
       
   820                         f = os.path.join(root, f)
       
   821                         self.choices.append((f, f.replace(path, "", 1)))
       
   822         else:
       
   823             try:
       
   824                 for f in os.listdir(self.path):
       
   825                     full_file = os.path.join(self.path, f)
       
   826                     if os.path.isfile(full_file) and (self.match is None or self.match_re.search(f)):
       
   827                         self.choices.append((full_file, f))
       
   828             except OSError:
       
   829                 pass
       
   830 
       
   831         self.widget.choices = self.choices
       
   832 
       
   833 class SplitDateTimeField(MultiValueField):
       
   834     widget = SplitDateTimeWidget
       
   835     hidden_widget = SplitHiddenDateTimeWidget
       
   836     default_error_messages = {
       
   837         'invalid_date': _(u'Enter a valid date.'),
       
   838         'invalid_time': _(u'Enter a valid time.'),
       
   839     }
       
   840 
       
   841     def __init__(self, input_date_formats=None, input_time_formats=None, *args, **kwargs):
       
   842         errors = self.default_error_messages.copy()
       
   843         if 'error_messages' in kwargs:
       
   844             errors.update(kwargs['error_messages'])
       
   845         localize = kwargs.get('localize', False)
       
   846         fields = (
       
   847             DateField(input_formats=input_date_formats,
       
   848                       error_messages={'invalid': errors['invalid_date']},
       
   849                       localize=localize),
       
   850             TimeField(input_formats=input_time_formats,
       
   851                       error_messages={'invalid': errors['invalid_time']},
       
   852                       localize=localize),
       
   853         )
       
   854         super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
       
   855 
       
   856     def compress(self, data_list):
       
   857         if data_list:
       
   858             # Raise a validation error if time or date is empty
       
   859             # (possible if SplitDateTimeField has required=False).
       
   860             if data_list[0] in validators.EMPTY_VALUES:
       
   861                 raise ValidationError(self.error_messages['invalid_date'])
       
   862             if data_list[1] in validators.EMPTY_VALUES:
       
   863                 raise ValidationError(self.error_messages['invalid_time'])
       
   864             return datetime.datetime.combine(*data_list)
       
   865         return None
       
   866 
       
   867 
       
   868 class IPAddressField(CharField):
       
   869     default_error_messages = {
       
   870         'invalid': _(u'Enter a valid IPv4 address.'),
       
   871     }
       
   872     default_validators = [validators.validate_ipv4_address]
       
   873 
       
   874 
       
   875 class SlugField(CharField):
       
   876     default_error_messages = {
       
   877         'invalid': _(u"Enter a valid 'slug' consisting of letters, numbers,"
       
   878                      u" underscores or hyphens."),
       
   879     }
       
   880     default_validators = [validators.validate_slug]