thirdparty/google_appengine/lib/django/django/newforms/fields.py
changeset 109 620f9b141567
equal deleted inserted replaced
108:261778de26ff 109:620f9b141567
       
     1 """
       
     2 Field classes
       
     3 """
       
     4 
       
     5 from django.utils.translation import gettext
       
     6 from util import ErrorList, ValidationError, smart_unicode
       
     7 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple
       
     8 import datetime
       
     9 import re
       
    10 import time
       
    11 
       
    12 __all__ = (
       
    13     'Field', 'CharField', 'IntegerField',
       
    14     'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
       
    15     'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
       
    16     'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
       
    17     'RegexField', 'EmailField', 'URLField', 'BooleanField',
       
    18     'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
       
    19     'ComboField', 'MultiValueField',
       
    20     'SplitDateTimeField',
       
    21 )
       
    22 
       
    23 # These values, if given to to_python(), will trigger the self.required check.
       
    24 EMPTY_VALUES = (None, '')
       
    25 
       
    26 try:
       
    27     set # Only available in Python 2.4+
       
    28 except NameError:
       
    29     from sets import Set as set # Python 2.3 fallback
       
    30 
       
    31 class Field(object):
       
    32     widget = TextInput # Default widget to use when rendering this type of Field.
       
    33     hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
       
    34 
       
    35     # Tracks each time a Field instance is created. Used to retain order.
       
    36     creation_counter = 0
       
    37 
       
    38     def __init__(self, required=True, widget=None, label=None, initial=None, help_text=None):
       
    39         # required -- Boolean that specifies whether the field is required.
       
    40         #             True by default.
       
    41         # widget -- A Widget class, or instance of a Widget class, that should be
       
    42         #         used for this Field when displaying it. Each Field has a default
       
    43         #         Widget that it'll use if you don't specify this. In most cases,
       
    44         #         the default widget is TextInput.
       
    45         # label -- A verbose name for this field, for use in displaying this field in
       
    46         #         a form. By default, Django will use a "pretty" version of the form
       
    47         #         field name, if the Field is part of a Form.
       
    48         # initial -- A value to use in this Field's initial display. This value is
       
    49         #            *not* used as a fallback if data isn't given.
       
    50         # help_text -- An optional string to use as "help text" for this Field.
       
    51         if label is not None:
       
    52             label = smart_unicode(label)
       
    53         self.required, self.label, self.initial = required, label, initial
       
    54         self.help_text = smart_unicode(help_text or '')
       
    55         widget = widget or self.widget
       
    56         if isinstance(widget, type):
       
    57             widget = widget()
       
    58 
       
    59         # Hook into self.widget_attrs() for any Field-specific HTML attributes.
       
    60         extra_attrs = self.widget_attrs(widget)
       
    61         if extra_attrs:
       
    62             widget.attrs.update(extra_attrs)
       
    63 
       
    64         self.widget = widget
       
    65 
       
    66         # Increase the creation counter, and save our local copy.
       
    67         self.creation_counter = Field.creation_counter
       
    68         Field.creation_counter += 1
       
    69 
       
    70     def clean(self, value):
       
    71         """
       
    72         Validates the given value and returns its "cleaned" value as an
       
    73         appropriate Python object.
       
    74 
       
    75         Raises ValidationError for any errors.
       
    76         """
       
    77         if self.required and value in EMPTY_VALUES:
       
    78             raise ValidationError(gettext(u'This field is required.'))
       
    79         return value
       
    80 
       
    81     def widget_attrs(self, widget):
       
    82         """
       
    83         Given a Widget instance (*not* a Widget class), returns a dictionary of
       
    84         any HTML attributes that should be added to the Widget, based on this
       
    85         Field.
       
    86         """
       
    87         return {}
       
    88 
       
    89 class CharField(Field):
       
    90     def __init__(self, max_length=None, min_length=None, *args, **kwargs):
       
    91         self.max_length, self.min_length = max_length, min_length
       
    92         super(CharField, self).__init__(*args, **kwargs)
       
    93 
       
    94     def clean(self, value):
       
    95         "Validates max_length and min_length. Returns a Unicode object."
       
    96         super(CharField, self).clean(value)
       
    97         if value in EMPTY_VALUES:
       
    98             return u''
       
    99         value = smart_unicode(value)
       
   100         if self.max_length is not None and len(value) > self.max_length:
       
   101             raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
       
   102         if self.min_length is not None and len(value) < self.min_length:
       
   103             raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
       
   104         return value
       
   105 
       
   106     def widget_attrs(self, widget):
       
   107         if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
       
   108             return {'maxlength': str(self.max_length)}
       
   109 
       
   110 class IntegerField(Field):
       
   111     def __init__(self, max_value=None, min_value=None, *args, **kwargs):
       
   112         self.max_value, self.min_value = max_value, min_value
       
   113         super(IntegerField, self).__init__(*args, **kwargs)
       
   114 
       
   115     def clean(self, value):
       
   116         """
       
   117         Validates that int() can be called on the input. Returns the result
       
   118         of int(). Returns None for empty values.
       
   119         """
       
   120         super(IntegerField, self).clean(value)
       
   121         if value in EMPTY_VALUES:
       
   122             return None
       
   123         try:
       
   124             value = int(value)
       
   125         except (ValueError, TypeError):
       
   126             raise ValidationError(gettext(u'Enter a whole number.'))
       
   127         if self.max_value is not None and value > self.max_value:
       
   128             raise ValidationError(gettext(u'Ensure this value is less than or equal to %s.') % self.max_value)
       
   129         if self.min_value is not None and value < self.min_value:
       
   130             raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value)
       
   131         return value
       
   132 
       
   133 DEFAULT_DATE_INPUT_FORMATS = (
       
   134     '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
       
   135     '%b %d %Y', '%b %d, %Y',            # 'Oct 25 2006', 'Oct 25, 2006'
       
   136     '%d %b %Y', '%d %b, %Y',            # '25 Oct 2006', '25 Oct, 2006'
       
   137     '%B %d %Y', '%B %d, %Y',            # 'October 25 2006', 'October 25, 2006'
       
   138     '%d %B %Y', '%d %B, %Y',            # '25 October 2006', '25 October, 2006'
       
   139 )
       
   140 
       
   141 class DateField(Field):
       
   142     def __init__(self, input_formats=None, *args, **kwargs):
       
   143         super(DateField, self).__init__(*args, **kwargs)
       
   144         self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
       
   145 
       
   146     def clean(self, value):
       
   147         """
       
   148         Validates that the input can be converted to a date. Returns a Python
       
   149         datetime.date object.
       
   150         """
       
   151         super(DateField, self).clean(value)
       
   152         if value in EMPTY_VALUES:
       
   153             return None
       
   154         if isinstance(value, datetime.datetime):
       
   155             return value.date()
       
   156         if isinstance(value, datetime.date):
       
   157             return value
       
   158         for format in self.input_formats:
       
   159             try:
       
   160                 return datetime.date(*time.strptime(value, format)[:3])
       
   161             except ValueError:
       
   162                 continue
       
   163         raise ValidationError(gettext(u'Enter a valid date.'))
       
   164 
       
   165 DEFAULT_TIME_INPUT_FORMATS = (
       
   166     '%H:%M:%S',     # '14:30:59'
       
   167     '%H:%M',        # '14:30'
       
   168 )
       
   169 
       
   170 class TimeField(Field):
       
   171     def __init__(self, input_formats=None, *args, **kwargs):
       
   172         super(TimeField, self).__init__(*args, **kwargs)
       
   173         self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
       
   174 
       
   175     def clean(self, value):
       
   176         """
       
   177         Validates that the input can be converted to a time. Returns a Python
       
   178         datetime.time object.
       
   179         """
       
   180         super(TimeField, self).clean(value)
       
   181         if value in EMPTY_VALUES:
       
   182             return None
       
   183         if isinstance(value, datetime.time):
       
   184             return value
       
   185         for format in self.input_formats:
       
   186             try:
       
   187                 return datetime.time(*time.strptime(value, format)[3:6])
       
   188             except ValueError:
       
   189                 continue
       
   190         raise ValidationError(gettext(u'Enter a valid time.'))
       
   191 
       
   192 DEFAULT_DATETIME_INPUT_FORMATS = (
       
   193     '%Y-%m-%d %H:%M:%S',     # '2006-10-25 14:30:59'
       
   194     '%Y-%m-%d %H:%M',        # '2006-10-25 14:30'
       
   195     '%Y-%m-%d',              # '2006-10-25'
       
   196     '%m/%d/%Y %H:%M:%S',     # '10/25/2006 14:30:59'
       
   197     '%m/%d/%Y %H:%M',        # '10/25/2006 14:30'
       
   198     '%m/%d/%Y',              # '10/25/2006'
       
   199     '%m/%d/%y %H:%M:%S',     # '10/25/06 14:30:59'
       
   200     '%m/%d/%y %H:%M',        # '10/25/06 14:30'
       
   201     '%m/%d/%y',              # '10/25/06'
       
   202 )
       
   203 
       
   204 class DateTimeField(Field):
       
   205     def __init__(self, input_formats=None, *args, **kwargs):
       
   206         super(DateTimeField, self).__init__(*args, **kwargs)
       
   207         self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
       
   208 
       
   209     def clean(self, value):
       
   210         """
       
   211         Validates that the input can be converted to a datetime. Returns a
       
   212         Python datetime.datetime object.
       
   213         """
       
   214         super(DateTimeField, self).clean(value)
       
   215         if value in EMPTY_VALUES:
       
   216             return None
       
   217         if isinstance(value, datetime.datetime):
       
   218             return value
       
   219         if isinstance(value, datetime.date):
       
   220             return datetime.datetime(value.year, value.month, value.day)
       
   221         for format in self.input_formats:
       
   222             try:
       
   223                 return datetime.datetime(*time.strptime(value, format)[:6])
       
   224             except ValueError:
       
   225                 continue
       
   226         raise ValidationError(gettext(u'Enter a valid date/time.'))
       
   227 
       
   228 class RegexField(Field):
       
   229     def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
       
   230         """
       
   231         regex can be either a string or a compiled regular expression object.
       
   232         error_message is an optional error message to use, if
       
   233         'Enter a valid value' is too generic for you.
       
   234         """
       
   235         super(RegexField, self).__init__(*args, **kwargs)
       
   236         if isinstance(regex, basestring):
       
   237             regex = re.compile(regex)
       
   238         self.regex = regex
       
   239         self.max_length, self.min_length = max_length, min_length
       
   240         self.error_message = error_message or gettext(u'Enter a valid value.')
       
   241 
       
   242     def clean(self, value):
       
   243         """
       
   244         Validates that the input matches the regular expression. Returns a
       
   245         Unicode object.
       
   246         """
       
   247         super(RegexField, self).clean(value)
       
   248         if value in EMPTY_VALUES:
       
   249             value = u''
       
   250         value = smart_unicode(value)
       
   251         if value == u'':
       
   252             return value
       
   253         if self.max_length is not None and len(value) > self.max_length:
       
   254             raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
       
   255         if self.min_length is not None and len(value) < self.min_length:
       
   256             raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
       
   257         if not self.regex.search(value):
       
   258             raise ValidationError(self.error_message)
       
   259         return value
       
   260 
       
   261 email_re = re.compile(
       
   262     r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom
       
   263     r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string
       
   264     r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE)  # domain
       
   265 
       
   266 class EmailField(RegexField):
       
   267     def __init__(self, max_length=None, min_length=None, *args, **kwargs):
       
   268         RegexField.__init__(self, email_re, max_length, min_length,
       
   269             gettext(u'Enter a valid e-mail address.'), *args, **kwargs)
       
   270 
       
   271 url_re = re.compile(
       
   272     r'^https?://' # http:// or https://
       
   273     r'(?:[A-Z0-9-]+\.)+[A-Z]{2,6}' # domain
       
   274     r'(?::\d+)?' # optional port
       
   275     r'(?:/?|/\S+)$', re.IGNORECASE)
       
   276 
       
   277 try:
       
   278     from django.conf import settings
       
   279     URL_VALIDATOR_USER_AGENT = settings.URL_VALIDATOR_USER_AGENT
       
   280 except ImportError:
       
   281     # It's OK if Django settings aren't configured.
       
   282     URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
       
   283 
       
   284 class URLField(RegexField):
       
   285     def __init__(self, max_length=None, min_length=None, verify_exists=False,
       
   286             validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):
       
   287         super(URLField, self).__init__(url_re, max_length, min_length, gettext(u'Enter a valid URL.'), *args, **kwargs)
       
   288         self.verify_exists = verify_exists
       
   289         self.user_agent = validator_user_agent
       
   290 
       
   291     def clean(self, value):
       
   292         value = super(URLField, self).clean(value)
       
   293         if value == u'':
       
   294             return value
       
   295         if self.verify_exists:
       
   296             import urllib2
       
   297             from django.conf import settings
       
   298             headers = {
       
   299                 "Accept": "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
       
   300                 "Accept-Language": "en-us,en;q=0.5",
       
   301                 "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
       
   302                 "Connection": "close",
       
   303                 "User-Agent": self.user_agent,
       
   304             }
       
   305             try:
       
   306                 req = urllib2.Request(value, None, headers)
       
   307                 u = urllib2.urlopen(req)
       
   308             except ValueError:
       
   309                 raise ValidationError(gettext(u'Enter a valid URL.'))
       
   310             except: # urllib2.URLError, httplib.InvalidURL, etc.
       
   311                 raise ValidationError(gettext(u'This URL appears to be a broken link.'))
       
   312         return value
       
   313 
       
   314 class BooleanField(Field):
       
   315     widget = CheckboxInput
       
   316 
       
   317     def clean(self, value):
       
   318         "Returns a Python boolean object."
       
   319         super(BooleanField, self).clean(value)
       
   320         return bool(value)
       
   321 
       
   322 class NullBooleanField(BooleanField):
       
   323     """
       
   324     A field whose valid values are None, True and False. Invalid values are
       
   325     cleaned to None.
       
   326     """
       
   327     widget = NullBooleanSelect
       
   328 
       
   329     def clean(self, value):
       
   330         return {True: True, False: False}.get(value, None)
       
   331 
       
   332 class ChoiceField(Field):
       
   333     def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None, help_text=None):
       
   334         super(ChoiceField, self).__init__(required, widget, label, initial, help_text)
       
   335         self.choices = choices
       
   336 
       
   337     def _get_choices(self):
       
   338         return self._choices
       
   339 
       
   340     def _set_choices(self, value):
       
   341         # Setting choices also sets the choices on the widget.
       
   342         # choices can be any iterable, but we call list() on it because
       
   343         # it will be consumed more than once.
       
   344         self._choices = self.widget.choices = list(value)
       
   345 
       
   346     choices = property(_get_choices, _set_choices)
       
   347 
       
   348     def clean(self, value):
       
   349         """
       
   350         Validates that the input is in self.choices.
       
   351         """
       
   352         value = super(ChoiceField, self).clean(value)
       
   353         if value in EMPTY_VALUES:
       
   354             value = u''
       
   355         value = smart_unicode(value)
       
   356         if value == u'':
       
   357             return value
       
   358         valid_values = set([str(k) for k, v in self.choices])
       
   359         if value not in valid_values:
       
   360             raise ValidationError(gettext(u'Select a valid choice. That choice is not one of the available choices.'))
       
   361         return value
       
   362 
       
   363 class MultipleChoiceField(ChoiceField):
       
   364     hidden_widget = MultipleHiddenInput
       
   365 
       
   366     def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None, help_text=None):
       
   367         super(MultipleChoiceField, self).__init__(choices, required, widget, label, initial, help_text)
       
   368 
       
   369     def clean(self, value):
       
   370         """
       
   371         Validates that the input is a list or tuple.
       
   372         """
       
   373         if self.required and not value:
       
   374             raise ValidationError(gettext(u'This field is required.'))
       
   375         elif not self.required and not value:
       
   376             return []
       
   377         if not isinstance(value, (list, tuple)):
       
   378             raise ValidationError(gettext(u'Enter a list of values.'))
       
   379         new_value = []
       
   380         for val in value:
       
   381             val = smart_unicode(val)
       
   382             new_value.append(val)
       
   383         # Validate that each value in the value list is in self.choices.
       
   384         valid_values = set([smart_unicode(k) for k, v in self.choices])
       
   385         for val in new_value:
       
   386             if val not in valid_values:
       
   387                 raise ValidationError(gettext(u'Select a valid choice. %s is not one of the available choices.') % val)
       
   388         return new_value
       
   389 
       
   390 class ComboField(Field):
       
   391     """
       
   392     A Field whose clean() method calls multiple Field clean() methods.
       
   393     """
       
   394     def __init__(self, fields=(), *args, **kwargs):
       
   395         super(ComboField, self).__init__(*args, **kwargs)
       
   396         # Set 'required' to False on the individual fields, because the
       
   397         # required validation will be handled by ComboField, not by those
       
   398         # individual fields.
       
   399         for f in fields:
       
   400             f.required = False
       
   401         self.fields = fields
       
   402 
       
   403     def clean(self, value):
       
   404         """
       
   405         Validates the given value against all of self.fields, which is a
       
   406         list of Field instances.
       
   407         """
       
   408         super(ComboField, self).clean(value)
       
   409         for field in self.fields:
       
   410             value = field.clean(value)
       
   411         return value
       
   412 
       
   413 class MultiValueField(Field):
       
   414     """
       
   415     A Field that is composed of multiple Fields.
       
   416 
       
   417     Its clean() method takes a "decompressed" list of values. Each value in
       
   418     this list is cleaned by the corresponding field -- the first value is
       
   419     cleaned by the first field, the second value is cleaned by the second
       
   420     field, etc. Once all fields are cleaned, the list of clean values is
       
   421     "compressed" into a single value.
       
   422 
       
   423     Subclasses should implement compress(), which specifies how a list of
       
   424     valid values should be converted to a single value. Subclasses should not
       
   425     have to implement clean().
       
   426 
       
   427     You'll probably want to use this with MultiWidget.
       
   428     """
       
   429     def __init__(self, fields=(), *args, **kwargs):
       
   430         super(MultiValueField, self).__init__(*args, **kwargs)
       
   431         # Set 'required' to False on the individual fields, because the
       
   432         # required validation will be handled by MultiValueField, not by those
       
   433         # individual fields.
       
   434         for f in fields:
       
   435             f.required = False
       
   436         self.fields = fields
       
   437 
       
   438     def clean(self, value):
       
   439         """
       
   440         Validates every value in the given list. A value is validated against
       
   441         the corresponding Field in self.fields.
       
   442 
       
   443         For example, if this MultiValueField was instantiated with
       
   444         fields=(DateField(), TimeField()), clean() would call
       
   445         DateField.clean(value[0]) and TimeField.clean(value[1]).
       
   446         """
       
   447         clean_data = []
       
   448         errors = ErrorList()
       
   449         if self.required and not value:
       
   450             raise ValidationError(gettext(u'This field is required.'))
       
   451         elif not self.required and not value:
       
   452             return self.compress([])
       
   453         if not isinstance(value, (list, tuple)):
       
   454             raise ValidationError(gettext(u'Enter a list of values.'))
       
   455         for i, field in enumerate(self.fields):
       
   456             try:
       
   457                 field_value = value[i]
       
   458             except KeyError:
       
   459                 field_value = None
       
   460             if self.required and field_value in EMPTY_VALUES:
       
   461                 raise ValidationError(gettext(u'This field is required.'))
       
   462             try:
       
   463                 clean_data.append(field.clean(field_value))
       
   464             except ValidationError, e:
       
   465                 # Collect all validation errors in a single list, which we'll
       
   466                 # raise at the end of clean(), rather than raising a single
       
   467                 # exception for the first error we encounter.
       
   468                 errors.extend(e.messages)
       
   469         if errors:
       
   470             raise ValidationError(errors)
       
   471         return self.compress(clean_data)
       
   472 
       
   473     def compress(self, data_list):
       
   474         """
       
   475         Returns a single value for the given list of values. The values can be
       
   476         assumed to be valid.
       
   477 
       
   478         For example, if this MultiValueField was instantiated with
       
   479         fields=(DateField(), TimeField()), this might return a datetime
       
   480         object created by combining the date and time in data_list.
       
   481         """
       
   482         raise NotImplementedError('Subclasses must implement this method.')
       
   483 
       
   484 class SplitDateTimeField(MultiValueField):
       
   485     def __init__(self, *args, **kwargs):
       
   486         fields = (DateField(), TimeField())
       
   487         super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
       
   488 
       
   489     def compress(self, data_list):
       
   490         if data_list:
       
   491             return datetime.datetime.combine(*data_list)
       
   492         return None