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