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