app/django/newforms/fields.py
changeset 54 03e267d67478
equal deleted inserted replaced
53:57b4279d8c4e 54:03e267d67478
       
     1 """
       
     2 Field classes.
       
     3 """
       
     4 
       
     5 import copy
       
     6 import datetime
       
     7 import os
       
     8 import re
       
     9 import time
       
    10 # Python 2.3 fallbacks
       
    11 try:
       
    12     from decimal import Decimal, DecimalException
       
    13 except ImportError:
       
    14     from django.utils._decimal import Decimal, DecimalException
       
    15 try:
       
    16     set
       
    17 except NameError:
       
    18     from sets import Set as set
       
    19 
       
    20 from django.utils.translation import ugettext_lazy as _
       
    21 from django.utils.encoding import StrAndUnicode, smart_unicode, smart_str
       
    22 
       
    23 from util import ErrorList, ValidationError
       
    24 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput
       
    25 
       
    26 
       
    27 __all__ = (
       
    28     'Field', 'CharField', 'IntegerField',
       
    29     'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
       
    30     'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
       
    31     'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
       
    32     'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField',
       
    33     'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
       
    34     'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
       
    35     'SplitDateTimeField', 'IPAddressField', 'FilePathField',
       
    36 )
       
    37 
       
    38 # These values, if given to to_python(), will trigger the self.required check.
       
    39 EMPTY_VALUES = (None, '')
       
    40 
       
    41 
       
    42 class Field(object):
       
    43     widget = TextInput # Default widget to use when rendering this type of Field.
       
    44     hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
       
    45     default_error_messages = {
       
    46         'required': _(u'This field is required.'),
       
    47         'invalid': _(u'Enter a valid value.'),
       
    48     }
       
    49 
       
    50     # Tracks each time a Field instance is created. Used to retain order.
       
    51     creation_counter = 0
       
    52 
       
    53     def __init__(self, required=True, widget=None, label=None, initial=None,
       
    54                  help_text=None, error_messages=None):
       
    55         # required -- Boolean that specifies whether the field is required.
       
    56         #             True by default.
       
    57         # widget -- A Widget class, or instance of a Widget class, that should
       
    58         #           be used for this Field when displaying it. Each Field has a
       
    59         #           default Widget that it'll use if you don't specify this. In
       
    60         #           most cases, the default widget is TextInput.
       
    61         # label -- A verbose name for this field, for use in displaying this
       
    62         #          field in a form. By default, Django will use a "pretty"
       
    63         #          version of the form field name, if the Field is part of a
       
    64         #          Form.
       
    65         # initial -- A value to use in this Field's initial display. This value
       
    66         #            is *not* used as a fallback if data isn't given.
       
    67         # help_text -- An optional string to use as "help text" for this Field.
       
    68         if label is not None:
       
    69             label = smart_unicode(label)
       
    70         self.required, self.label, self.initial = required, label, initial
       
    71         self.help_text = smart_unicode(help_text or '')
       
    72         widget = widget or self.widget
       
    73         if isinstance(widget, type):
       
    74             widget = widget()
       
    75 
       
    76         # Hook into self.widget_attrs() for any Field-specific HTML attributes.
       
    77         extra_attrs = self.widget_attrs(widget)
       
    78         if extra_attrs:
       
    79             widget.attrs.update(extra_attrs)
       
    80 
       
    81         self.widget = widget
       
    82 
       
    83         # Increase the creation counter, and save our local copy.
       
    84         self.creation_counter = Field.creation_counter
       
    85         Field.creation_counter += 1
       
    86 
       
    87         def set_class_error_messages(messages, klass):
       
    88             for base_class in klass.__bases__:
       
    89                 set_class_error_messages(messages, base_class)
       
    90             messages.update(getattr(klass, 'default_error_messages', {}))
       
    91 
       
    92         messages = {}
       
    93         set_class_error_messages(messages, self.__class__)
       
    94         messages.update(error_messages or {})
       
    95         self.error_messages = messages
       
    96 
       
    97     def clean(self, value):
       
    98         """
       
    99         Validates the given value and returns its "cleaned" value as an
       
   100         appropriate Python object.
       
   101 
       
   102         Raises ValidationError for any errors.
       
   103         """
       
   104         if self.required and value in EMPTY_VALUES:
       
   105             raise ValidationError(self.error_messages['required'])
       
   106         return value
       
   107 
       
   108     def widget_attrs(self, widget):
       
   109         """
       
   110         Given a Widget instance (*not* a Widget class), returns a dictionary of
       
   111         any HTML attributes that should be added to the Widget, based on this
       
   112         Field.
       
   113         """
       
   114         return {}
       
   115 
       
   116     def __deepcopy__(self, memo):
       
   117         result = copy.copy(self)
       
   118         memo[id(self)] = result
       
   119         result.widget = copy.deepcopy(self.widget, memo)
       
   120         return result
       
   121 
       
   122 class CharField(Field):
       
   123     default_error_messages = {
       
   124         'max_length': _(u'Ensure this value has at most %(max)d characters (it has %(length)d).'),
       
   125         'min_length': _(u'Ensure this value has at least %(min)d characters (it has %(length)d).'),
       
   126     }
       
   127 
       
   128     def __init__(self, max_length=None, min_length=None, *args, **kwargs):
       
   129         self.max_length, self.min_length = max_length, min_length
       
   130         super(CharField, self).__init__(*args, **kwargs)
       
   131 
       
   132     def clean(self, value):
       
   133         "Validates max_length and min_length. Returns a Unicode object."
       
   134         super(CharField, self).clean(value)
       
   135         if value in EMPTY_VALUES:
       
   136             return u''
       
   137         value = smart_unicode(value)
       
   138         value_length = len(value)
       
   139         if self.max_length is not None and value_length > self.max_length:
       
   140             raise ValidationError(self.error_messages['max_length'] % {'max': self.max_length, 'length': value_length})
       
   141         if self.min_length is not None and value_length < self.min_length:
       
   142             raise ValidationError(self.error_messages['min_length'] % {'min': self.min_length, 'length': value_length})
       
   143         return value
       
   144 
       
   145     def widget_attrs(self, widget):
       
   146         if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
       
   147             # The HTML attribute is maxlength, not max_length.
       
   148             return {'maxlength': str(self.max_length)}
       
   149 
       
   150 class IntegerField(Field):
       
   151     default_error_messages = {
       
   152         'invalid': _(u'Enter a whole number.'),
       
   153         'max_value': _(u'Ensure this value is less than or equal to %s.'),
       
   154         'min_value': _(u'Ensure this value is greater than or equal to %s.'),
       
   155     }
       
   156 
       
   157     def __init__(self, max_value=None, min_value=None, *args, **kwargs):
       
   158         self.max_value, self.min_value = max_value, min_value
       
   159         super(IntegerField, self).__init__(*args, **kwargs)
       
   160 
       
   161     def clean(self, value):
       
   162         """
       
   163         Validates that int() can be called on the input. Returns the result
       
   164         of int(). Returns None for empty values.
       
   165         """
       
   166         super(IntegerField, self).clean(value)
       
   167         if value in EMPTY_VALUES:
       
   168             return None
       
   169         try:
       
   170             value = int(str(value))
       
   171         except (ValueError, TypeError):
       
   172             raise ValidationError(self.error_messages['invalid'])
       
   173         if self.max_value is not None and value > self.max_value:
       
   174             raise ValidationError(self.error_messages['max_value'] % self.max_value)
       
   175         if self.min_value is not None and value < self.min_value:
       
   176             raise ValidationError(self.error_messages['min_value'] % self.min_value)
       
   177         return value
       
   178 
       
   179 class FloatField(Field):
       
   180     default_error_messages = {
       
   181         'invalid': _(u'Enter a number.'),
       
   182         'max_value': _(u'Ensure this value is less than or equal to %s.'),
       
   183         'min_value': _(u'Ensure this value is greater than or equal to %s.'),
       
   184     }
       
   185 
       
   186     def __init__(self, max_value=None, min_value=None, *args, **kwargs):
       
   187         self.max_value, self.min_value = max_value, min_value
       
   188         Field.__init__(self, *args, **kwargs)
       
   189 
       
   190     def clean(self, value):
       
   191         """
       
   192         Validates that float() can be called on the input. Returns a float.
       
   193         Returns None for empty values.
       
   194         """
       
   195         super(FloatField, self).clean(value)
       
   196         if not self.required and value in EMPTY_VALUES:
       
   197             return None
       
   198         try:
       
   199             value = float(value)
       
   200         except (ValueError, TypeError):
       
   201             raise ValidationError(self.error_messages['invalid'])
       
   202         if self.max_value is not None and value > self.max_value:
       
   203             raise ValidationError(self.error_messages['max_value'] % self.max_value)
       
   204         if self.min_value is not None and value < self.min_value:
       
   205             raise ValidationError(self.error_messages['min_value'] % self.min_value)
       
   206         return value
       
   207 
       
   208 class DecimalField(Field):
       
   209     default_error_messages = {
       
   210         'invalid': _(u'Enter a number.'),
       
   211         'max_value': _(u'Ensure this value is less than or equal to %s.'),
       
   212         'min_value': _(u'Ensure this value is greater than or equal to %s.'),
       
   213         'max_digits': _('Ensure that there are no more than %s digits in total.'),
       
   214         'max_decimal_places': _('Ensure that there are no more than %s decimal places.'),
       
   215         'max_whole_digits': _('Ensure that there are no more than %s digits before the decimal point.')
       
   216     }
       
   217 
       
   218     def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs):
       
   219         self.max_value, self.min_value = max_value, min_value
       
   220         self.max_digits, self.decimal_places = max_digits, decimal_places
       
   221         Field.__init__(self, *args, **kwargs)
       
   222 
       
   223     def clean(self, value):
       
   224         """
       
   225         Validates that the input is a decimal number. Returns a Decimal
       
   226         instance. Returns None for empty values. Ensures that there are no more
       
   227         than max_digits in the number, and no more than decimal_places digits
       
   228         after the decimal point.
       
   229         """
       
   230         super(DecimalField, self).clean(value)
       
   231         if not self.required and value in EMPTY_VALUES:
       
   232             return None
       
   233         value = smart_str(value).strip()
       
   234         try:
       
   235             value = Decimal(value)
       
   236         except DecimalException:
       
   237             raise ValidationError(self.error_messages['invalid'])
       
   238         pieces = str(value).lstrip("-").split('.')
       
   239         decimals = (len(pieces) == 2) and len(pieces[1]) or 0
       
   240         digits = len(pieces[0])
       
   241         if self.max_value is not None and value > self.max_value:
       
   242             raise ValidationError(self.error_messages['max_value'] % self.max_value)
       
   243         if self.min_value is not None and value < self.min_value:
       
   244             raise ValidationError(self.error_messages['min_value'] % self.min_value)
       
   245         if self.max_digits is not None and (digits + decimals) > self.max_digits:
       
   246             raise ValidationError(self.error_messages['max_digits'] % self.max_digits)
       
   247         if self.decimal_places is not None and decimals > self.decimal_places:
       
   248             raise ValidationError(self.error_messages['max_decimal_places'] % self.decimal_places)
       
   249         if self.max_digits is not None and self.decimal_places is not None and digits > (self.max_digits - self.decimal_places):
       
   250             raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
       
   251         return value
       
   252 
       
   253 DEFAULT_DATE_INPUT_FORMATS = (
       
   254     '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
       
   255     '%b %d %Y', '%b %d, %Y',            # 'Oct 25 2006', 'Oct 25, 2006'
       
   256     '%d %b %Y', '%d %b, %Y',            # '25 Oct 2006', '25 Oct, 2006'
       
   257     '%B %d %Y', '%B %d, %Y',            # 'October 25 2006', 'October 25, 2006'
       
   258     '%d %B %Y', '%d %B, %Y',            # '25 October 2006', '25 October, 2006'
       
   259 )
       
   260 
       
   261 class DateField(Field):
       
   262     default_error_messages = {
       
   263         'invalid': _(u'Enter a valid date.'),
       
   264     }
       
   265 
       
   266     def __init__(self, input_formats=None, *args, **kwargs):
       
   267         super(DateField, self).__init__(*args, **kwargs)
       
   268         self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
       
   269 
       
   270     def clean(self, value):
       
   271         """
       
   272         Validates that the input can be converted to a date. Returns a Python
       
   273         datetime.date object.
       
   274         """
       
   275         super(DateField, self).clean(value)
       
   276         if value in EMPTY_VALUES:
       
   277             return None
       
   278         if isinstance(value, datetime.datetime):
       
   279             return value.date()
       
   280         if isinstance(value, datetime.date):
       
   281             return value
       
   282         for format in self.input_formats:
       
   283             try:
       
   284                 return datetime.date(*time.strptime(value, format)[:3])
       
   285             except ValueError:
       
   286                 continue
       
   287         raise ValidationError(self.error_messages['invalid'])
       
   288 
       
   289 DEFAULT_TIME_INPUT_FORMATS = (
       
   290     '%H:%M:%S',     # '14:30:59'
       
   291     '%H:%M',        # '14:30'
       
   292 )
       
   293 
       
   294 class TimeField(Field):
       
   295     default_error_messages = {
       
   296         'invalid': _(u'Enter a valid time.')
       
   297     }
       
   298 
       
   299     def __init__(self, input_formats=None, *args, **kwargs):
       
   300         super(TimeField, self).__init__(*args, **kwargs)
       
   301         self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
       
   302 
       
   303     def clean(self, value):
       
   304         """
       
   305         Validates that the input can be converted to a time. Returns a Python
       
   306         datetime.time object.
       
   307         """
       
   308         super(TimeField, self).clean(value)
       
   309         if value in EMPTY_VALUES:
       
   310             return None
       
   311         if isinstance(value, datetime.time):
       
   312             return value
       
   313         for format in self.input_formats:
       
   314             try:
       
   315                 return datetime.time(*time.strptime(value, format)[3:6])
       
   316             except ValueError:
       
   317                 continue
       
   318         raise ValidationError(self.error_messages['invalid'])
       
   319 
       
   320 DEFAULT_DATETIME_INPUT_FORMATS = (
       
   321     '%Y-%m-%d %H:%M:%S',     # '2006-10-25 14:30:59'
       
   322     '%Y-%m-%d %H:%M',        # '2006-10-25 14:30'
       
   323     '%Y-%m-%d',              # '2006-10-25'
       
   324     '%m/%d/%Y %H:%M:%S',     # '10/25/2006 14:30:59'
       
   325     '%m/%d/%Y %H:%M',        # '10/25/2006 14:30'
       
   326     '%m/%d/%Y',              # '10/25/2006'
       
   327     '%m/%d/%y %H:%M:%S',     # '10/25/06 14:30:59'
       
   328     '%m/%d/%y %H:%M',        # '10/25/06 14:30'
       
   329     '%m/%d/%y',              # '10/25/06'
       
   330 )
       
   331 
       
   332 class DateTimeField(Field):
       
   333     widget = DateTimeInput
       
   334     default_error_messages = {
       
   335         'invalid': _(u'Enter a valid date/time.'),
       
   336     }
       
   337 
       
   338     def __init__(self, input_formats=None, *args, **kwargs):
       
   339         super(DateTimeField, self).__init__(*args, **kwargs)
       
   340         self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
       
   341 
       
   342     def clean(self, value):
       
   343         """
       
   344         Validates that the input can be converted to a datetime. Returns a
       
   345         Python datetime.datetime object.
       
   346         """
       
   347         super(DateTimeField, self).clean(value)
       
   348         if value in EMPTY_VALUES:
       
   349             return None
       
   350         if isinstance(value, datetime.datetime):
       
   351             return value
       
   352         if isinstance(value, datetime.date):
       
   353             return datetime.datetime(value.year, value.month, value.day)
       
   354         if isinstance(value, list):
       
   355             # Input comes from a SplitDateTimeWidget, for example. So, it's two
       
   356             # components: date and time.
       
   357             if len(value) != 2:
       
   358                 raise ValidationError(self.error_messages['invalid'])
       
   359             value = '%s %s' % tuple(value)
       
   360         for format in self.input_formats:
       
   361             try:
       
   362                 return datetime.datetime(*time.strptime(value, format)[:6])
       
   363             except ValueError:
       
   364                 continue
       
   365         raise ValidationError(self.error_messages['invalid'])
       
   366 
       
   367 class RegexField(CharField):
       
   368     def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
       
   369         """
       
   370         regex can be either a string or a compiled regular expression object.
       
   371         error_message is an optional error message to use, if
       
   372         'Enter a valid value' is too generic for you.
       
   373         """
       
   374         # error_message is just kept for backwards compatibility:
       
   375         if error_message:
       
   376             error_messages = kwargs.get('error_messages') or {}
       
   377             error_messages['invalid'] = error_message
       
   378             kwargs['error_messages'] = error_messages
       
   379         super(RegexField, self).__init__(max_length, min_length, *args, **kwargs)
       
   380         if isinstance(regex, basestring):
       
   381             regex = re.compile(regex)
       
   382         self.regex = regex
       
   383 
       
   384     def clean(self, value):
       
   385         """
       
   386         Validates that the input matches the regular expression. Returns a
       
   387         Unicode object.
       
   388         """
       
   389         value = super(RegexField, self).clean(value)
       
   390         if value == u'':
       
   391             return value
       
   392         if not self.regex.search(value):
       
   393             raise ValidationError(self.error_messages['invalid'])
       
   394         return value
       
   395 
       
   396 email_re = re.compile(
       
   397     r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom
       
   398     r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string
       
   399     r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE)  # domain
       
   400 
       
   401 class EmailField(RegexField):
       
   402     default_error_messages = {
       
   403         'invalid': _(u'Enter a valid e-mail address.'),
       
   404     }
       
   405 
       
   406     def __init__(self, max_length=None, min_length=None, *args, **kwargs):
       
   407         RegexField.__init__(self, email_re, max_length, min_length, *args,
       
   408                             **kwargs)
       
   409 
       
   410 try:
       
   411     from django.conf import settings
       
   412     URL_VALIDATOR_USER_AGENT = settings.URL_VALIDATOR_USER_AGENT
       
   413 except ImportError:
       
   414     # It's OK if Django settings aren't configured.
       
   415     URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
       
   416 
       
   417 class UploadedFile(StrAndUnicode):
       
   418     "A wrapper for files uploaded in a FileField"
       
   419     def __init__(self, filename, content):
       
   420         self.filename = filename
       
   421         self.content = content
       
   422 
       
   423     def __unicode__(self):
       
   424         """
       
   425         The unicode representation is the filename, so that the pre-database-insertion
       
   426         logic can use UploadedFile objects
       
   427         """
       
   428         return self.filename
       
   429 
       
   430 class FileField(Field):
       
   431     widget = FileInput
       
   432     default_error_messages = {
       
   433         'invalid': _(u"No file was submitted. Check the encoding type on the form."),
       
   434         'missing': _(u"No file was submitted."),
       
   435         'empty': _(u"The submitted file is empty."),
       
   436     }
       
   437 
       
   438     def __init__(self, *args, **kwargs):
       
   439         super(FileField, self).__init__(*args, **kwargs)
       
   440 
       
   441     def clean(self, data, initial=None):
       
   442         super(FileField, self).clean(initial or data)
       
   443         if not self.required and data in EMPTY_VALUES:
       
   444             return None
       
   445         elif not data and initial:
       
   446             return initial
       
   447         try:
       
   448             f = UploadedFile(data['filename'], data['content'])
       
   449         except TypeError:
       
   450             raise ValidationError(self.error_messages['invalid'])
       
   451         except KeyError:
       
   452             raise ValidationError(self.error_messages['missing'])
       
   453         if not f.content:
       
   454             raise ValidationError(self.error_messages['empty'])
       
   455         return f
       
   456 
       
   457 class ImageField(FileField):
       
   458     default_error_messages = {
       
   459         'invalid_image': _(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."),
       
   460     }
       
   461 
       
   462     def clean(self, data, initial=None):
       
   463         """
       
   464         Checks that the file-upload field data contains a valid image (GIF, JPG,
       
   465         PNG, possibly others -- whatever the Python Imaging Library supports).
       
   466         """
       
   467         f = super(ImageField, self).clean(data, initial)
       
   468         if f is None:
       
   469             return None
       
   470         elif not data and initial:
       
   471             return initial
       
   472         from PIL import Image
       
   473         from cStringIO import StringIO
       
   474         try:
       
   475             # load() is the only method that can spot a truncated JPEG,
       
   476             #  but it cannot be called sanely after verify()
       
   477             trial_image = Image.open(StringIO(f.content))
       
   478             trial_image.load()
       
   479             # verify() is the only method that can spot a corrupt PNG,
       
   480             #  but it must be called immediately after the constructor
       
   481             trial_image = Image.open(StringIO(f.content))
       
   482             trial_image.verify()
       
   483         except Exception: # Python Imaging Library doesn't recognize it as an image
       
   484             raise ValidationError(self.error_messages['invalid_image'])
       
   485         return f
       
   486 
       
   487 url_re = re.compile(
       
   488     r'^https?://' # http:// or https://
       
   489     r'(?:(?:[A-Z0-9-]+\.)+[A-Z]{2,6}|' #domain...
       
   490     r'localhost|' #localhost...
       
   491     r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
       
   492     r'(?::\d+)?' # optional port
       
   493     r'(?:/?|/\S+)$', re.IGNORECASE)
       
   494 
       
   495 class URLField(RegexField):
       
   496     default_error_messages = {
       
   497         'invalid': _(u'Enter a valid URL.'),
       
   498         'invalid_link': _(u'This URL appears to be a broken link.'),
       
   499     }
       
   500 
       
   501     def __init__(self, max_length=None, min_length=None, verify_exists=False,
       
   502             validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):
       
   503         super(URLField, self).__init__(url_re, max_length, min_length, *args,
       
   504                                        **kwargs)
       
   505         self.verify_exists = verify_exists
       
   506         self.user_agent = validator_user_agent
       
   507 
       
   508     def clean(self, value):
       
   509         # If no URL scheme given, assume http://
       
   510         if value and '://' not in value:
       
   511             value = u'http://%s' % value
       
   512         value = super(URLField, self).clean(value)
       
   513         if value == u'':
       
   514             return value
       
   515         if self.verify_exists:
       
   516             import urllib2
       
   517             from django.conf import settings
       
   518             headers = {
       
   519                 "Accept": "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
       
   520                 "Accept-Language": "en-us,en;q=0.5",
       
   521                 "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
       
   522                 "Connection": "close",
       
   523                 "User-Agent": self.user_agent,
       
   524             }
       
   525             try:
       
   526                 req = urllib2.Request(value, None, headers)
       
   527                 u = urllib2.urlopen(req)
       
   528             except ValueError:
       
   529                 raise ValidationError(self.error_messages['invalid'])
       
   530             except: # urllib2.URLError, httplib.InvalidURL, etc.
       
   531                 raise ValidationError(self.error_messages['invalid_link'])
       
   532         return value
       
   533 
       
   534 class BooleanField(Field):
       
   535     widget = CheckboxInput
       
   536 
       
   537     def clean(self, value):
       
   538         """Returns a Python boolean object."""
       
   539         super(BooleanField, self).clean(value)
       
   540         # Explicitly check for the string 'False', which is what a hidden field
       
   541         # will submit for False. Because bool("True") == True, we don't need to
       
   542         # handle that explicitly.
       
   543         if value == 'False':
       
   544             return False
       
   545         return bool(value)
       
   546 
       
   547 class NullBooleanField(BooleanField):
       
   548     """
       
   549     A field whose valid values are None, True and False. Invalid values are
       
   550     cleaned to None.
       
   551     """
       
   552     widget = NullBooleanSelect
       
   553 
       
   554     def clean(self, value):
       
   555         return {True: True, False: False}.get(value, None)
       
   556 
       
   557 class ChoiceField(Field):
       
   558     widget = Select
       
   559     default_error_messages = {
       
   560         'invalid_choice': _(u'Select a valid choice. That choice is not one of the available choices.'),
       
   561     }
       
   562 
       
   563     def __init__(self, choices=(), required=True, widget=None, label=None,
       
   564                  initial=None, help_text=None, *args, **kwargs):
       
   565         super(ChoiceField, self).__init__(required, widget, label, initial,
       
   566                                           help_text, *args, **kwargs)
       
   567         self.choices = choices
       
   568 
       
   569     def _get_choices(self):
       
   570         return self._choices
       
   571 
       
   572     def _set_choices(self, value):
       
   573         # Setting choices also sets the choices on the widget.
       
   574         # choices can be any iterable, but we call list() on it because
       
   575         # it will be consumed more than once.
       
   576         self._choices = self.widget.choices = list(value)
       
   577 
       
   578     choices = property(_get_choices, _set_choices)
       
   579 
       
   580     def clean(self, value):
       
   581         """
       
   582         Validates that the input is in self.choices.
       
   583         """
       
   584         value = super(ChoiceField, self).clean(value)
       
   585         if value in EMPTY_VALUES:
       
   586             value = u''
       
   587         value = smart_unicode(value)
       
   588         if value == u'':
       
   589             return value
       
   590         valid_values = set([smart_unicode(k) for k, v in self.choices])
       
   591         if value not in valid_values:
       
   592             raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
       
   593         return value
       
   594 
       
   595 class MultipleChoiceField(ChoiceField):
       
   596     hidden_widget = MultipleHiddenInput
       
   597     widget = SelectMultiple
       
   598     default_error_messages = {
       
   599         'invalid_choice': _(u'Select a valid choice. %(value)s is not one of the available choices.'),
       
   600         'invalid_list': _(u'Enter a list of values.'),
       
   601     }
       
   602 
       
   603     def clean(self, value):
       
   604         """
       
   605         Validates that the input is a list or tuple.
       
   606         """
       
   607         if self.required and not value:
       
   608             raise ValidationError(self.error_messages['required'])
       
   609         elif not self.required and not value:
       
   610             return []
       
   611         if not isinstance(value, (list, tuple)):
       
   612             raise ValidationError(self.error_messages['invalid_list'])
       
   613         new_value = [smart_unicode(val) for val in value]
       
   614         # Validate that each value in the value list is in self.choices.
       
   615         valid_values = set([smart_unicode(k) for k, v in self.choices])
       
   616         for val in new_value:
       
   617             if val not in valid_values:
       
   618                 raise ValidationError(self.error_messages['invalid_choice'] % {'value': val})
       
   619         return new_value
       
   620 
       
   621 class ComboField(Field):
       
   622     """
       
   623     A Field whose clean() method calls multiple Field clean() methods.
       
   624     """
       
   625     def __init__(self, fields=(), *args, **kwargs):
       
   626         super(ComboField, self).__init__(*args, **kwargs)
       
   627         # Set 'required' to False on the individual fields, because the
       
   628         # required validation will be handled by ComboField, not by those
       
   629         # individual fields.
       
   630         for f in fields:
       
   631             f.required = False
       
   632         self.fields = fields
       
   633 
       
   634     def clean(self, value):
       
   635         """
       
   636         Validates the given value against all of self.fields, which is a
       
   637         list of Field instances.
       
   638         """
       
   639         super(ComboField, self).clean(value)
       
   640         for field in self.fields:
       
   641             value = field.clean(value)
       
   642         return value
       
   643 
       
   644 class MultiValueField(Field):
       
   645     """
       
   646     A Field that aggregates the logic of multiple Fields.
       
   647 
       
   648     Its clean() method takes a "decompressed" list of values, which are then
       
   649     cleaned into a single value according to self.fields. Each value in
       
   650     this list is cleaned by the corresponding field -- the first value is
       
   651     cleaned by the first field, the second value is cleaned by the second
       
   652     field, etc. Once all fields are cleaned, the list of clean values is
       
   653     "compressed" into a single value.
       
   654 
       
   655     Subclasses should not have to implement clean(). Instead, they must
       
   656     implement compress(), which takes a list of valid values and returns a
       
   657     "compressed" version of those values -- a single value.
       
   658 
       
   659     You'll probably want to use this with MultiWidget.
       
   660     """
       
   661     default_error_messages = {
       
   662         'invalid': _(u'Enter a list of values.'),
       
   663     }
       
   664 
       
   665     def __init__(self, fields=(), *args, **kwargs):
       
   666         super(MultiValueField, self).__init__(*args, **kwargs)
       
   667         # Set 'required' to False on the individual fields, because the
       
   668         # required validation will be handled by MultiValueField, not by those
       
   669         # individual fields.
       
   670         for f in fields:
       
   671             f.required = False
       
   672         self.fields = fields
       
   673 
       
   674     def clean(self, value):
       
   675         """
       
   676         Validates every value in the given list. A value is validated against
       
   677         the corresponding Field in self.fields.
       
   678 
       
   679         For example, if this MultiValueField was instantiated with
       
   680         fields=(DateField(), TimeField()), clean() would call
       
   681         DateField.clean(value[0]) and TimeField.clean(value[1]).
       
   682         """
       
   683         clean_data = []
       
   684         errors = ErrorList()
       
   685         if not value or isinstance(value, (list, tuple)):
       
   686             if not value or not [v for v in value if v not in EMPTY_VALUES]:
       
   687                 if self.required:
       
   688                     raise ValidationError(self.error_messages['required'])
       
   689                 else:
       
   690                     return self.compress([])
       
   691         else:
       
   692             raise ValidationError(self.error_messages['invalid'])
       
   693         for i, field in enumerate(self.fields):
       
   694             try:
       
   695                 field_value = value[i]
       
   696             except IndexError:
       
   697                 field_value = None
       
   698             if self.required and field_value in EMPTY_VALUES:
       
   699                 raise ValidationError(self.error_messages['required'])
       
   700             try:
       
   701                 clean_data.append(field.clean(field_value))
       
   702             except ValidationError, e:
       
   703                 # Collect all validation errors in a single list, which we'll
       
   704                 # raise at the end of clean(), rather than raising a single
       
   705                 # exception for the first error we encounter.
       
   706                 errors.extend(e.messages)
       
   707         if errors:
       
   708             raise ValidationError(errors)
       
   709         return self.compress(clean_data)
       
   710 
       
   711     def compress(self, data_list):
       
   712         """
       
   713         Returns a single value for the given list of values. The values can be
       
   714         assumed to be valid.
       
   715 
       
   716         For example, if this MultiValueField was instantiated with
       
   717         fields=(DateField(), TimeField()), this might return a datetime
       
   718         object created by combining the date and time in data_list.
       
   719         """
       
   720         raise NotImplementedError('Subclasses must implement this method.')
       
   721 
       
   722 class FilePathField(ChoiceField):
       
   723     def __init__(self, path, match=None, recursive=False, required=True,
       
   724                  widget=Select, label=None, initial=None, help_text=None,
       
   725                  *args, **kwargs):
       
   726         self.path, self.match, self.recursive = path, match, recursive
       
   727         super(FilePathField, self).__init__(choices=(), required=required,
       
   728             widget=widget, label=label, initial=initial, help_text=help_text,
       
   729             *args, **kwargs)
       
   730         self.choices = []
       
   731         if self.match is not None:
       
   732             self.match_re = re.compile(self.match)
       
   733         if recursive:
       
   734             for root, dirs, files in os.walk(self.path):
       
   735                 for f in files:
       
   736                     if self.match is None or self.match_re.search(f):
       
   737                         f = os.path.join(root, f)
       
   738                         self.choices.append((f, f.replace(path, "", 1)))
       
   739         else:
       
   740             try:
       
   741                 for f in os.listdir(self.path):
       
   742                     full_file = os.path.join(self.path, f)
       
   743                     if os.path.isfile(full_file) and (self.match is None or self.match_re.search(f)):
       
   744                         self.choices.append((full_file, f))
       
   745             except OSError:
       
   746                 pass
       
   747         self.widget.choices = self.choices
       
   748 
       
   749 class SplitDateTimeField(MultiValueField):
       
   750     default_error_messages = {
       
   751         'invalid_date': _(u'Enter a valid date.'),
       
   752         'invalid_time': _(u'Enter a valid time.'),
       
   753     }
       
   754 
       
   755     def __init__(self, *args, **kwargs):
       
   756         errors = self.default_error_messages.copy()
       
   757         if 'error_messages' in kwargs:
       
   758             errors.update(kwargs['error_messages'])
       
   759         fields = (
       
   760             DateField(error_messages={'invalid': errors['invalid_date']}),
       
   761             TimeField(error_messages={'invalid': errors['invalid_time']}),
       
   762         )
       
   763         super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
       
   764 
       
   765     def compress(self, data_list):
       
   766         if data_list:
       
   767             # Raise a validation error if time or date is empty
       
   768             # (possible if SplitDateTimeField has required=False).
       
   769             if data_list[0] in EMPTY_VALUES:
       
   770                 raise ValidationError(self.error_messages['invalid_date'])
       
   771             if data_list[1] in EMPTY_VALUES:
       
   772                 raise ValidationError(self.error_messages['invalid_time'])
       
   773             return datetime.datetime.combine(*data_list)
       
   774         return None
       
   775 
       
   776 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}$')
       
   777 
       
   778 class IPAddressField(RegexField):
       
   779     default_error_messages = {
       
   780         'invalid': _(u'Enter a valid IPv4 address.'),
       
   781     }
       
   782 
       
   783     def __init__(self, *args, **kwargs):
       
   784         super(IPAddressField, self).__init__(ipv4_re, *args, **kwargs)