Ticket #3457: newforms_error_messages.3.patch

File newforms_error_messages.3.patch, 40.6 KB (added by Chris Beaven, 17 years ago)

with full tests and docs

  • django/newforms/fields.py

     
    66import re
    77import time
    88
    9 from django.utils.translation import ugettext
     9from django.utils.translation import ugettext_lazy
    1010from django.utils.encoding import StrAndUnicode, smart_unicode
    1111
    1212from util import ErrorList, ValidationError
     
    4444class Field(object):
    4545    widget = TextInput # Default widget to use when rendering this type of Field.
    4646    hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
     47    default_error_messages = {
     48        'required': ugettext_lazy(u'This field is required.'),
     49        'invalid': ugettext_lazy(u'Enter a valid value.'),
     50    }
    4751
    4852    # Tracks each time a Field instance is created. Used to retain order.
    4953    creation_counter = 0
    5054
    51     def __init__(self, required=True, widget=None, label=None, initial=None, help_text=None):
     55    def __init__(self, required=True, widget=None, label=None, initial=None,
     56                 help_text=None, error_messages=None):
    5257        # required -- Boolean that specifies whether the field is required.
    5358        #             True by default.
    5459        # widget -- A Widget class, or instance of a Widget class, that should
     
    8186        self.creation_counter = Field.creation_counter
    8287        Field.creation_counter += 1
    8388
     89        self.error_messages = self._build_error_messages(error_messages)
     90
     91    def _build_error_messages(self, extra_error_messages):
     92        error_messages = {}
     93        def get_default_error_messages(klass):
     94            for base_class in klass.__bases__:
     95                get_default_error_messages(base_class)
     96            if hasattr(klass, 'default_error_messages'):
     97                error_messages.update(klass.default_error_messages)
     98        get_default_error_messages(self.__class__)
     99        if extra_error_messages:
     100            error_messages.update(extra_error_messages)
     101        return error_messages
     102
    84103    def clean(self, value):
    85104        """
    86105        Validates the given value and returns its "cleaned" value as an
     
    89108        Raises ValidationError for any errors.
    90109        """
    91110        if self.required and value in EMPTY_VALUES:
    92             raise ValidationError(ugettext(u'This field is required.'))
     111            raise ValidationError(self.error_messages['required'])
    93112        return value
    94113
    95114    def widget_attrs(self, widget):
     
    101120        return {}
    102121
    103122class CharField(Field):
     123    default_error_messages = {
     124        'max_length': ugettext_lazy(u'Ensure this value has at most %(max)d characters (it has %(length)d).'),
     125        'min_length': ugettext_lazy(u'Ensure this value has at least %(min)d characters (it has %(length)d).'),
     126    }
     127
    104128    def __init__(self, max_length=None, min_length=None, *args, **kwargs):
    105129        self.max_length, self.min_length = max_length, min_length
    106130        super(CharField, self).__init__(*args, **kwargs)
     
    113137        value = smart_unicode(value)
    114138        value_length = len(value)
    115139        if self.max_length is not None and value_length > self.max_length:
    116             raise ValidationError(ugettext(u'Ensure this value has at most %(max)d characters (it has %(length)d).') % {'max': self.max_length, 'length': value_length})
     140            raise ValidationError(self.error_messages['max_length'] % {'max': self.max_length, 'length': value_length})
    117141        if self.min_length is not None and value_length < self.min_length:
    118             raise ValidationError(ugettext(u'Ensure this value has at least %(min)d characters (it has %(length)d).') % {'min': self.min_length, 'length': value_length})
     142            raise ValidationError(self.error_messages['min_length'] % {'min': self.min_length, 'length': value_length})
    119143        return value
    120144
    121145    def widget_attrs(self, widget):
     
    124148            return {'maxlength': str(self.max_length)}
    125149
    126150class IntegerField(Field):
     151    default_error_messages = {
     152        'invalid': ugettext_lazy(u'Enter a whole number.'),
     153        'max_value': ugettext_lazy(u'Ensure this value is less than or equal to %s.'),
     154        'min_value': ugettext_lazy(u'Ensure this value is greater than or equal to %s.'),
     155    }
     156
    127157    def __init__(self, max_value=None, min_value=None, *args, **kwargs):
    128158        self.max_value, self.min_value = max_value, min_value
    129159        super(IntegerField, self).__init__(*args, **kwargs)
     
    139169        try:
    140170            value = int(value)
    141171        except (ValueError, TypeError):
    142             raise ValidationError(ugettext(u'Enter a whole number.'))
     172            raise ValidationError(self.error_messages['invalid'])
    143173        if self.max_value is not None and value > self.max_value:
    144             raise ValidationError(ugettext(u'Ensure this value is less than or equal to %s.') % self.max_value)
     174            raise ValidationError(self.error_messages['max_value'] % self.max_value)
    145175        if self.min_value is not None and value < self.min_value:
    146             raise ValidationError(ugettext(u'Ensure this value is greater than or equal to %s.') % self.min_value)
     176            raise ValidationError(self.error_messages['min_value'] % self.min_value)
    147177        return value
    148178
    149179class FloatField(Field):
     180    default_error_messages = {
     181        'invalid': ugettext_lazy(u'Enter a number.'),
     182        'max_value': ugettext_lazy(u'Ensure this value is less than or equal to %s.'),
     183        'min_value': ugettext_lazy(u'Ensure this value is greater than or equal to %s.'),
     184    }
     185
    150186    def __init__(self, max_value=None, min_value=None, *args, **kwargs):
    151187        self.max_value, self.min_value = max_value, min_value
    152188        Field.__init__(self, *args, **kwargs)
     
    162198        try:
    163199            value = float(value)
    164200        except (ValueError, TypeError):
    165             raise ValidationError(ugettext('Enter a number.'))
     201            raise ValidationError(self.error_messages['invalid'])
    166202        if self.max_value is not None and value > self.max_value:
    167             raise ValidationError(ugettext('Ensure this value is less than or equal to %s.') % self.max_value)
     203            raise ValidationError(self.error_messages['max_value'] % self.max_value)
    168204        if self.min_value is not None and value < self.min_value:
    169             raise ValidationError(ugettext('Ensure this value is greater than or equal to %s.') % self.min_value)
     205            raise ValidationError(self.error_messages['min_value'] % self.min_value)
    170206        return value
    171207
    172208class DecimalField(Field):
     209    default_error_messages = {
     210        'invalid': ugettext_lazy(u'Enter a number.'),
     211        'max_value': ugettext_lazy(u'Ensure this value is less than or equal to %s.'),
     212        'min_value': ugettext_lazy(u'Ensure this value is greater than or equal to %s.'),
     213        'max_digits': ugettext_lazy('Ensure that there are no more than %s digits in total.'),
     214        'max_decimal_places': ugettext_lazy('Ensure that there are no more than %s decimal places.'),
     215        'max_whole_digits': ugettext_lazy('Ensure that there are no more than %s digits before the decimal point.')
     216    }
     217
    173218    def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs):
    174219        self.max_value, self.min_value = max_value, min_value
    175220        self.max_digits, self.decimal_places = max_digits, decimal_places
     
    189234        try:
    190235            value = Decimal(value)
    191236        except DecimalException:
    192             raise ValidationError(ugettext('Enter a number.'))
     237            raise ValidationError(self.error_messages['invalid'])
    193238        pieces = str(value).lstrip("-").split('.')
    194239        decimals = (len(pieces) == 2) and len(pieces[1]) or 0
    195240        digits = len(pieces[0])
    196241        if self.max_value is not None and value > self.max_value:
    197             raise ValidationError(ugettext('Ensure this value is less than or equal to %s.') % self.max_value)
     242            raise ValidationError(self.error_messages['max_value'] % self.max_value)
    198243        if self.min_value is not None and value < self.min_value:
    199             raise ValidationError(ugettext('Ensure this value is greater than or equal to %s.') % self.min_value)
     244            raise ValidationError(self.error_messages['min_value'] % self.min_value)
    200245        if self.max_digits is not None and (digits + decimals) > self.max_digits:
    201             raise ValidationError(ugettext('Ensure that there are no more than %s digits in total.') % self.max_digits)
     246            raise ValidationError(self.error_messages['max_digits'] % self.max_digits)
    202247        if self.decimal_places is not None and decimals > self.decimal_places:
    203             raise ValidationError(ugettext('Ensure that there are no more than %s decimal places.') % self.decimal_places)
     248            raise ValidationError(self.error_messages['max_decimal_places'] % self.decimal_places)
    204249        if self.max_digits is not None and self.decimal_places is not None and digits > (self.max_digits - self.decimal_places):
    205             raise ValidationError(ugettext('Ensure that there are no more than %s digits before the decimal point.') % (self.max_digits - self.decimal_places))
     250            raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
    206251        return value
    207252
    208253DEFAULT_DATE_INPUT_FORMATS = (
     
    214259)
    215260
    216261class DateField(Field):
     262    default_error_messages = {
     263        'invalid': ugettext_lazy(u'Enter a valid date.'),
     264    }
     265
    217266    def __init__(self, input_formats=None, *args, **kwargs):
    218267        super(DateField, self).__init__(*args, **kwargs)
    219268        self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
     
    235284                return datetime.date(*time.strptime(value, format)[:3])
    236285            except ValueError:
    237286                continue
    238         raise ValidationError(ugettext(u'Enter a valid date.'))
     287        raise ValidationError(self.error_messages['invalid'])
    239288
    240289DEFAULT_TIME_INPUT_FORMATS = (
    241290    '%H:%M:%S',     # '14:30:59'
     
    243292)
    244293
    245294class TimeField(Field):
     295    default_error_messages = {
     296        'invalid': ugettext_lazy(u'Enter a valid time.')
     297    }
     298
    246299    def __init__(self, input_formats=None, *args, **kwargs):
    247300        super(TimeField, self).__init__(*args, **kwargs)
    248301        self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
     
    262315                return datetime.time(*time.strptime(value, format)[3:6])
    263316            except ValueError:
    264317                continue
    265         raise ValidationError(ugettext(u'Enter a valid time.'))
     318        raise ValidationError(self.error_messages['invalid'])
    266319
    267320DEFAULT_DATETIME_INPUT_FORMATS = (
    268321    '%Y-%m-%d %H:%M:%S',     # '2006-10-25 14:30:59'
     
    277330)
    278331
    279332class DateTimeField(Field):
     333    default_error_messages = {
     334        'invalid': ugettext_lazy(u'Enter a valid date/time.'),
     335    }
     336
    280337    def __init__(self, input_formats=None, *args, **kwargs):
    281338        super(DateTimeField, self).__init__(*args, **kwargs)
    282339        self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
     
    298355                return datetime.datetime(*time.strptime(value, format)[:6])
    299356            except ValueError:
    300357                continue
    301         raise ValidationError(ugettext(u'Enter a valid date/time.'))
     358        raise ValidationError(self.error_messages['invalid'])
    302359
    303360class RegexField(CharField):
    304361    def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs):
     
    307364        error_message is an optional error message to use, if
    308365        'Enter a valid value' is too generic for you.
    309366        """
     367        # error_message is just kept for backwards compatibility:
     368        if error_message:
     369            error_messages = kwargs.get('error_messages') or {}
     370            error_messages['invalid'] = error_message
     371            kwargs['error_messages'] = error_messages
    310372        super(RegexField, self).__init__(max_length, min_length, *args, **kwargs)
    311373        if isinstance(regex, basestring):
    312374            regex = re.compile(regex)
    313375        self.regex = regex
    314         self.error_message = error_message or ugettext(u'Enter a valid value.')
    315376
    316377    def clean(self, value):
    317378        """
     
    322383        if value == u'':
    323384            return value
    324385        if not self.regex.search(value):
    325             raise ValidationError(self.error_message)
     386            raise ValidationError(self.error_messages['invalid'])
    326387        return value
    327388
    328389email_re = re.compile(
     
    331392    r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE)  # domain
    332393
    333394class EmailField(RegexField):
     395    default_error_messages = {
     396        'invalid': ugettext_lazy(u'Enter a valid e-mail address.'),
     397    }
     398
    334399    def __init__(self, max_length=None, min_length=None, *args, **kwargs):
    335         RegexField.__init__(self, email_re, max_length, min_length,
    336             ugettext(u'Enter a valid e-mail address.'), *args, **kwargs)
     400        RegexField.__init__(self, email_re, max_length, min_length, *args,
     401                            **kwargs)
    337402
    338403url_re = re.compile(
    339404    r'^https?://' # http:// or https://
     
    363428
    364429class FileField(Field):
    365430    widget = FileInput
     431    default_error_messages = {
     432        'invalid': ugettext_lazy(u"No file was submitted. Check the encoding type on the form."),
     433        'missing': ugettext_lazy(u"No file was submitted."),
     434        'empty': ugettext_lazy(u"The submitted file is empty."),
     435    }
     436
    366437    def __init__(self, *args, **kwargs):
    367438        super(FileField, self).__init__(*args, **kwargs)
    368439
     
    373444        try:
    374445            f = UploadedFile(data['filename'], data['content'])
    375446        except TypeError:
    376             raise ValidationError(ugettext(u"No file was submitted. Check the encoding type on the form."))
     447            raise ValidationError(self.error_messages['invalid'])
    377448        except KeyError:
    378             raise ValidationError(ugettext(u"No file was submitted."))
     449            raise ValidationError(self.error_messages['missing'])
    379450        if not f.content:
    380             raise ValidationError(ugettext(u"The submitted file is empty."))
     451            raise ValidationError(self.error_messages['empty'])
    381452        return f
    382453
    383454class ImageField(FileField):
     455    default_error_messages = {
     456        'invalid_image': ugettext_lazy(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."),
     457    }
     458
    384459    def clean(self, data):
    385460        """
    386461        Checks that the file-upload field data contains a valid image (GIF, JPG,
     
    394469        try:
    395470            Image.open(StringIO(f.content))
    396471        except IOError: # Python Imaging Library doesn't recognize it as an image
    397             raise ValidationError(ugettext(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."))
     472            raise ValidationError(self.error_messages['invalid_image'])
    398473        return f
    399474
    400475class URLField(RegexField):
     476    default_error_messages = {
     477        'invalid': ugettext_lazy(u'Enter a valid URL.'),
     478        'invalid_link': ugettext_lazy(u'This URL appears to be a broken link.'),
     479    }
     480
    401481    def __init__(self, max_length=None, min_length=None, verify_exists=False,
    402482            validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):
    403         super(URLField, self).__init__(url_re, max_length, min_length, ugettext(u'Enter a valid URL.'), *args, **kwargs)
     483        super(URLField, self).__init__(url_re, max_length, min_length, *args,
     484                                       **kwargs)
    404485        self.verify_exists = verify_exists
    405486        self.user_agent = validator_user_agent
    406487
     
    422503                req = urllib2.Request(value, None, headers)
    423504                u = urllib2.urlopen(req)
    424505            except ValueError:
    425                 raise ValidationError(ugettext(u'Enter a valid URL.'))
     506                raise ValidationError(self.error_messages['invalid'])
    426507            except: # urllib2.URLError, httplib.InvalidURL, etc.
    427                 raise ValidationError(ugettext(u'This URL appears to be a broken link.'))
     508                raise ValidationError(self.error_messages['invalid_link'])
    428509        return value
    429510
    430511class BooleanField(Field):
     
    447528
    448529class ChoiceField(Field):
    449530    widget = Select
     531    default_error_messages = {
     532        'invalid_choice': ugettext_lazy(u'Select a valid choice. That choice is not one of the available choices.'),
     533    }
    450534
    451     def __init__(self, choices=(), required=True, widget=None, label=None, initial=None, help_text=None):
    452         super(ChoiceField, self).__init__(required, widget, label, initial, help_text)
     535    def __init__(self, choices=(), required=True, widget=None, label=None,
     536                 initial=None, help_text=None, *args, **kwargs):
     537        super(ChoiceField, self).__init__(required, widget, label, initial,
     538                                          help_text, *args, **kwargs)
    453539        self.choices = choices
    454540
    455541    def _get_choices(self):
     
    475561            return value
    476562        valid_values = set([smart_unicode(k) for k, v in self.choices])
    477563        if value not in valid_values:
    478             raise ValidationError(ugettext(u'Select a valid choice. That choice is not one of the available choices.'))
     564            raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
    479565        return value
    480566
    481567class MultipleChoiceField(ChoiceField):
    482568    hidden_widget = MultipleHiddenInput
    483569    widget = SelectMultiple
     570    default_error_messages = {
     571        'invalid_choice': ugettext_lazy(u'Select a valid choice. %(value)s is not one of the available choices.'),
     572        'invalid_list': ugettext_lazy(u'Enter a list of values.'),
     573    }
    484574
    485575    def clean(self, value):
    486576        """
    487577        Validates that the input is a list or tuple.
    488578        """
    489579        if self.required and not value:
    490             raise ValidationError(ugettext(u'This field is required.'))
     580            raise ValidationError(self.error_messages['required'])
    491581        elif not self.required and not value:
    492582            return []
    493583        if not isinstance(value, (list, tuple)):
    494             raise ValidationError(ugettext(u'Enter a list of values.'))
     584            raise ValidationError(self.error_messages['invalid_list'])
    495585        new_value = [smart_unicode(val) for val in value]
    496586        # Validate that each value in the value list is in self.choices.
    497587        valid_values = set([smart_unicode(k) for k, v in self.choices])
    498588        for val in new_value:
    499589            if val not in valid_values:
    500                 raise ValidationError(ugettext(u'Select a valid choice. %s is not one of the available choices.') % val)
     590                raise ValidationError(self.error_messages['invalid_choice'] % {'value': val})
    501591        return new_value
    502592
    503593class ComboField(Field):
     
    540630
    541631    You'll probably want to use this with MultiWidget.
    542632    """
     633    default_error_messages = {
     634        'invalid': ugettext_lazy(u'Enter a list of values.'),
     635    }
    543636    def __init__(self, fields=(), *args, **kwargs):
    544637        super(MultiValueField, self).__init__(*args, **kwargs)
    545638        # Set 'required' to False on the individual fields, because the
     
    563656        if not value or isinstance(value, (list, tuple)):
    564657            if not value or not [v for v in value if v not in EMPTY_VALUES]:
    565658                if self.required:
    566                     raise ValidationError(ugettext(u'This field is required.'))
     659                    raise ValidationError(self.error_messages['required'])
    567660                else:
    568661                    return self.compress([])
    569662        else:
    570             raise ValidationError(ugettext(u'Enter a list of values.'))
     663            raise ValidationError(self.error_messages['invalid'])
    571664        for i, field in enumerate(self.fields):
    572665            try:
    573666                field_value = value[i]
    574667            except IndexError:
    575668                field_value = None
    576669            if self.required and field_value in EMPTY_VALUES:
    577                 raise ValidationError(ugettext(u'This field is required.'))
     670                raise ValidationError(self.error_messages['required'])
    578671            try:
    579672                clean_data.append(field.clean(field_value))
    580673            except ValidationError, e:
     
    598691        raise NotImplementedError('Subclasses must implement this method.')
    599692
    600693class SplitDateTimeField(MultiValueField):
     694    default_error_messages = {
     695        'invalid_date': ugettext_lazy(u'Enter a valid date.'),
     696        'invalid_time': ugettext_lazy(u'Enter a valid time.'),
     697    }
    601698    def __init__(self, *args, **kwargs):
    602         fields = (DateField(), TimeField())
     699        e = self.default_error_messages.copy()
     700        if 'error_messages' in kwargs:
     701            e.update(kwargs['error_messages'])
     702        fields = (DateField(error_messages={'invalid': e['invalid_date']}),
     703                  TimeField(error_messages={'invalid': e['invalid_time']}))
    603704        super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
    604705
    605706    def compress(self, data_list):
     
    607708            # Raise a validation error if time or date is empty
    608709            # (possible if SplitDateTimeField has required=False).
    609710            if data_list[0] in EMPTY_VALUES:
    610                 raise ValidationError(ugettext(u'Enter a valid date.'))
     711                raise ValidationError(self.error_messages['invalid_date'])
    611712            if data_list[1] in EMPTY_VALUES:
    612                 raise ValidationError(ugettext(u'Enter a valid time.'))
     713                raise ValidationError(self.error_messages['invalid_time'])
    613714            return datetime.datetime.combine(*data_list)
    614715        return None
  • django/newforms/util.py

     
    11from django.utils.html import escape
    2 from django.utils.encoding import smart_unicode, StrAndUnicode
     2from django.utils.encoding import smart_unicode, force_unicode, StrAndUnicode
    33
    44def flatatt(attrs):
    55    """
     
    4141        if not self: return u''
    4242        return u'\n'.join([u'* %s' % smart_unicode(e) for e in self])
    4343
     44    def __repr__(self):
     45        return repr([force_unicode(e) for e in self])
     46
    4447class ValidationError(Exception):
    4548    def __init__(self, message):
    4649        "ValidationError can be passed a string or a list."
    4750        if isinstance(message, list):
    4851            self.messages = ErrorList([smart_unicode(msg) for msg in message])
    4952        else:
    50             assert isinstance(message, basestring), ("%s should be a basestring" % repr(message))
    5153            message = smart_unicode(message)
    5254            self.messages = ErrorList([message])
    5355
  • docs/newforms.txt

     
    997997    <p>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</p>
    998998    <p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
    999999
     1000``error_messages``
     1001~~~~~~~~~~~~~~~~~~
     1002
     1003The ``error_messages`` argument lets you override the default messages which the
     1004field will raise. Pass in a dictionary with keys matching the error messages you
     1005want to override. For example::
     1006
     1007    >>> generic = forms.CharField()
     1008    >>> generic.clean('')
     1009    Traceback (most recent call last):
     1010      ...
     1011    ValidationError: [u'This field is required.']
     1012
     1013    >>> name = forms.CharField(error_messages={'required': 'Please enter your name'})
     1014    >>> name.clean('')
     1015    Traceback (most recent call last):
     1016      ...
     1017    ValidationError: [u'Please enter your name']
     1018
     1019In the `built-in Field classes`_ section below, each Field defines the error
     1020message keys it uses. 
     1021
    10001022Dynamic initial values
    10011023----------------------
    10021024
     
    10611083    * Empty value: ``None``
    10621084    * Normalizes to: A Python ``True`` or ``False`` value.
    10631085    * Validates nothing (i.e., it never raises a ``ValidationError``).
     1086    * Error message keys: ``required``
    10641087
    10651088``CharField``
    10661089~~~~~~~~~~~~~
     
    10691092    * Empty value: ``''`` (an empty string)
    10701093    * Normalizes to: A Unicode object.
    10711094    * Validates nothing, unless ``max_length`` or ``min_length`` is provided.
     1095    * Error message keys: ``required``, ``max_length``, ``min_length``
    10721096
    10731097Has two optional arguments for validation, ``max_length`` and ``min_length``.
    10741098If provided, these arguments ensure that the string is at most or at least the
     
    10811105    * Empty value: ``''`` (an empty string)
    10821106    * Normalizes to: A Unicode object.
    10831107    * Validates that the given value exists in the list of choices.
     1108    * Error message keys: ``required``, ``invalid_choice``
    10841109
    10851110Takes one extra argument, ``choices``, which is an iterable (e.g., a list or
    10861111tuple) of 2-tuples to use as choices for this field.
     
    10931118    * Normalizes to: A Python ``datetime.date`` object.
    10941119    * Validates that the given value is either a ``datetime.date``,
    10951120      ``datetime.datetime`` or string formatted in a particular date format.
     1121    * Error message keys: ``required``, ``invalid``
    10961122
    10971123Takes one optional argument, ``input_formats``, which is a list of formats used
    10981124to attempt to convert a string to a valid ``datetime.date`` object.
     
    11131139    * Normalizes to: A Python ``datetime.datetime`` object.
    11141140    * Validates that the given value is either a ``datetime.datetime``,
    11151141      ``datetime.date`` or string formatted in a particular datetime format.
     1142    * Error message keys: ``required``, ``invalid``
    11161143
    11171144Takes one optional argument, ``input_formats``, which is a list of formats used
    11181145to attempt to convert a string to a valid ``datetime.datetime`` object.
     
    11391166    * Normalizes to: A Python ``decimal``.
    11401167    * Validates that the given value is a decimal. Leading and trailing
    11411168      whitespace is ignored.
     1169    * Error message keys: ``required``, ``invalid``, ``max_value``,
     1170      ``min_value``, ``max_digits``, ``max_decimal_places``,
     1171      ``max_whole_digits``
    11421172
    11431173Takes four optional arguments: ``max_value``, ``min_value``, ``max_digits``,
    11441174and ``decimal_places``. The first two define the limits for the fields value.
     
    11551185    * Normalizes to: A Unicode object.
    11561186    * Validates that the given value is a valid e-mail address, using a
    11571187      moderately complex regular expression.
     1188    * Error message keys: ``required``, ``invalid``
    11581189
    11591190Has two optional arguments for validation, ``max_length`` and ``min_length``.
    11601191If provided, these arguments ensure that the string is at most or at least the
     
    11701201    * Normalizes to: An ``UploadedFile`` object that wraps the file content
    11711202      and file name into a single object.
    11721203    * Validates that non-empty file data has been bound to the form.
     1204    * Error message keys: ``required``, ``invalid``, ``missing``, ``empty``
    11731205
    11741206An ``UploadedFile`` object has two attributes:
    11751207
     
    12001232      and file name into a single object.
    12011233    * Validates that file data has been bound to the form, and that the
    12021234      file is of an image format understood by PIL.
     1235    * Error message keys: ``required``, ``invalid``, ``missing``, ``empty``,
     1236      ``invalid_image``
    12031237
    12041238Using an ImageField requires that the `Python Imaging Library`_ is installed.
    12051239
     
    12161250    * Normalizes to: A Python integer or long integer.
    12171251    * Validates that the given value is an integer. Leading and trailing
    12181252      whitespace is allowed, as in Python's ``int()`` function.
     1253    * Error message keys: ``required``, ``invalid``, ``max_value``,
     1254      ``min_value``
    12191255
    12201256Takes two optional arguments for validation, ``max_value`` and ``min_value``.
    12211257These control the range of values permitted in the field.
     
    12281264    * Normalizes to: A list of Unicode objects.
    12291265    * Validates that every value in the given list of values exists in the list
    12301266      of choices.
     1267    * Error message keys: ``required``, ``invalid_choice``, ``invalid_list``
    12311268
    12321269Takes one extra argument, ``choices``, which is an iterable (e.g., a list or
    12331270tuple) of 2-tuples to use as choices for this field.
     
    12481285    * Normalizes to: A Unicode object.
    12491286    * Validates that the given value matches against a certain regular
    12501287      expression.
     1288    * Error message keys: ``required``, ``invalid``
    12511289
    12521290Takes one required argument, ``regex``, which is a regular expression specified
    12531291either as a string or a compiled regular expression object.
     
    12591297    ======================  =====================================================
    12601298    ``max_length``          Ensures the string has at most this many characters.
    12611299    ``min_length``          Ensures the string has at least this many characters.
    1262     ``error_message``       Error message to return for failed validation. If no
    1263                             message is provided, a generic error message will be
    1264                             used.
    12651300    ======================  =====================================================
    12661301
     1302The optional argument ``error_message`` is also accepted for backwards
     1303compatibility. The preferred way to provide an error message is to use the
     1304``error_messages`` argument, passing a dictionary with ``'invalid'`` as a key
     1305and the error message as the value.
     1306
    12671307``TimeField``
    12681308~~~~~~~~~~~~~
    12691309
     
    12721312    * Normalizes to: A Python ``datetime.time`` object.
    12731313    * Validates that the given value is either a ``datetime.time`` or string
    12741314      formatted in a particular time format.
     1315    * Error message keys: ``required``, ``invalid``
    12751316
    12761317Takes one optional argument, ``input_formats``, which is a list of formats used
    12771318to attempt to convert a string to a valid ``datetime.time`` object.
     
    12881329    * Empty value: ``''`` (an empty string)
    12891330    * Normalizes to: A Unicode object.
    12901331    * Validates that the given value is a valid URL.
     1332    * Error message keys: ``required``, ``invalid``, ``invalid_link``
    12911333
    12921334Takes the following optional arguments:
    12931335
  • tests/regressiontests/forms/tests.py

     
    939939>>> f.clean('1234567890a')
    940940u'1234567890a'
    941941
     942Custom error messages
     943>>> e = {'required': 'REQUIRED'}
     944>>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s'
     945>>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s'
     946>>> f = CharField(min_length=5, max_length=10, error_messages=e)
     947>>> f.clean('')
     948Traceback (most recent call last):
     949...
     950ValidationError: [u'REQUIRED']
     951>>> f.clean('1234')
     952Traceback (most recent call last):
     953...
     954ValidationError: [u'LENGTH 4, MIN LENGTH 5']
     955>>> f.clean('12345678901')
     956Traceback (most recent call last):
     957...
     958ValidationError: [u'LENGTH 11, MAX LENGTH 10']
     959
    942960# IntegerField ################################################################
    943961
    944962>>> f = IntegerField()
     
    10641082...
    10651083ValidationError: [u'Ensure this value is less than or equal to 20.']
    10661084
     1085Custom error messages
     1086>>> e = {'required': 'REQUIRED'}
     1087>>> e['invalid'] = 'INVALID'
     1088>>> e['min_value'] = 'MIN VALUE IS %s'
     1089>>> e['max_value'] = 'MAX VALUE IS %s'
     1090>>> f = IntegerField(min_value=5, max_value=10, error_messages=e)
     1091>>> f.clean('')
     1092Traceback (most recent call last):
     1093...
     1094ValidationError: [u'REQUIRED']
     1095>>> f.clean('abc')
     1096Traceback (most recent call last):
     1097...
     1098ValidationError: [u'INVALID']
     1099>>> f.clean('4')
     1100Traceback (most recent call last):
     1101...
     1102ValidationError: [u'MIN VALUE IS 5']
     1103>>> f.clean('11')
     1104Traceback (most recent call last):
     1105...
     1106ValidationError: [u'MAX VALUE IS 10']
     1107
    10671108# FloatField ##################################################################
    10681109
    10691110>>> f = FloatField()
     
    11221163>>> f.clean('0.5')
    112311640.5
    11241165
     1166Custom error messages
     1167>>> e = {'required': 'REQUIRED'}
     1168>>> e['invalid'] = 'INVALID'
     1169>>> e['min_value'] = 'MIN VALUE IS %s'
     1170>>> e['max_value'] = 'MAX VALUE IS %s'
     1171>>> f = FloatField(min_value=5, max_value=10, error_messages=e)
     1172>>> f.clean('')
     1173Traceback (most recent call last):
     1174...
     1175ValidationError: [u'REQUIRED']
     1176>>> f.clean('abc')
     1177Traceback (most recent call last):
     1178...
     1179ValidationError: [u'INVALID']
     1180>>> f.clean('4')
     1181Traceback (most recent call last):
     1182...
     1183ValidationError: [u'MIN VALUE IS 5']
     1184>>> f.clean('11')
     1185Traceback (most recent call last):
     1186...
     1187ValidationError: [u'MAX VALUE IS 10']
     1188
    11251189# DecimalField ################################################################
    11261190
    11271191>>> f = DecimalField(max_digits=4, decimal_places=2)
     
    12201284>>> f.clean('00.50')
    12211285Decimal("0.50")
    12221286
     1287Custom error messages
     1288>>> e = {'required': 'REQUIRED'}
     1289>>> e['invalid'] = 'INVALID'
     1290>>> e['min_value'] = 'MIN VALUE IS %s'
     1291>>> e['max_value'] = 'MAX VALUE IS %s'
     1292>>> e['max_digits'] = 'MAX DIGITS IS %s'
     1293>>> e['max_decimal_places'] = 'MAX DP IS %s'
     1294>>> e['max_whole_digits'] = 'MAX DIGITS BEFORE DP IS %s'
     1295>>> f = DecimalField(min_value=5, max_value=10, error_messages=e)
     1296>>> f2 = DecimalField(max_digits=4, decimal_places=2, error_messages=e)
     1297>>> f.clean('')
     1298Traceback (most recent call last):
     1299...
     1300ValidationError: [u'REQUIRED']
     1301>>> f.clean('abc')
     1302Traceback (most recent call last):
     1303...
     1304ValidationError: [u'INVALID']
     1305>>> f.clean('4')
     1306Traceback (most recent call last):
     1307...
     1308ValidationError: [u'MIN VALUE IS 5']
     1309>>> f.clean('11')
     1310Traceback (most recent call last):
     1311...
     1312ValidationError: [u'MAX VALUE IS 10']
     1313>>> f2.clean('123.45')
     1314Traceback (most recent call last):
     1315...
     1316ValidationError: [u'MAX DIGITS IS 4']
     1317>>> f2.clean('1.234')
     1318Traceback (most recent call last):
     1319...
     1320ValidationError: [u'MAX DP IS 2']
     1321>>> f2.clean('123.4')
     1322Traceback (most recent call last):
     1323...
     1324ValidationError: [u'MAX DIGITS BEFORE DP IS 2']
     1325
    12231326# DateField ###################################################################
    12241327
    12251328>>> import datetime
     
    12971400...
    12981401ValidationError: [u'Enter a valid date.']
    12991402
     1403Custom error messages
     1404>>> e = {'required': 'REQUIRED'}
     1405>>> e['invalid'] = 'INVALID'
     1406>>> f = DateField(error_messages=e)
     1407>>> f.clean('')
     1408Traceback (most recent call last):
     1409...
     1410ValidationError: [u'REQUIRED']
     1411>>> f.clean('abc')
     1412Traceback (most recent call last):
     1413...
     1414ValidationError: [u'INVALID']
     1415
    13001416# TimeField ###################################################################
    13011417
    13021418>>> import datetime
     
    13361452...
    13371453ValidationError: [u'Enter a valid time.']
    13381454
     1455Custom error messages
     1456>>> e = {'required': 'REQUIRED'}
     1457>>> e['invalid'] = 'INVALID'
     1458>>> f = TimeField(error_messages=e)
     1459>>> f.clean('')
     1460Traceback (most recent call last):
     1461...
     1462ValidationError: [u'REQUIRED']
     1463>>> f.clean('abc')
     1464Traceback (most recent call last):
     1465...
     1466ValidationError: [u'INVALID']
     1467
    13391468# DateTimeField ###############################################################
    13401469
    13411470>>> import datetime
     
    14091538>>> repr(f.clean(''))
    14101539'None'
    14111540
     1541Custom error messages
     1542>>> e = {'required': 'REQUIRED'}
     1543>>> e['invalid'] = 'INVALID'
     1544>>> f = DateTimeField(error_messages=e)
     1545>>> f.clean('')
     1546Traceback (most recent call last):
     1547...
     1548ValidationError: [u'REQUIRED']
     1549>>> f.clean('abc')
     1550Traceback (most recent call last):
     1551...
     1552ValidationError: [u'INVALID']
     1553
    14121554# RegexField ##################################################################
    14131555
    14141556>>> f = RegexField('^\d[A-F]\d$')
     
    15001642...
    15011643ValidationError: [u'Enter a valid value.']
    15021644
     1645Custom error messages
     1646>>> e = {'required': 'REQUIRED'}
     1647>>> e['invalid'] = 'INVALID'
     1648>>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s'
     1649>>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s'
     1650>>> f = RegexField(r'^\d+$', min_length=5, max_length=10, error_messages=e)
     1651>>> f.clean('')
     1652Traceback (most recent call last):
     1653...
     1654ValidationError: [u'REQUIRED']
     1655>>> f.clean('abcde')
     1656Traceback (most recent call last):
     1657...
     1658ValidationError: [u'INVALID']
     1659>>> f.clean('1234')
     1660Traceback (most recent call last):
     1661...
     1662ValidationError: [u'LENGTH 4, MIN LENGTH 5']
     1663>>> f.clean('12345678901')
     1664Traceback (most recent call last):
     1665...
     1666ValidationError: [u'LENGTH 11, MAX LENGTH 10']
     1667
    15031668# EmailField ##################################################################
    15041669
    15051670>>> f = EmailField()
     
    15591724...
    15601725ValidationError: [u'Ensure this value has at most 15 characters (it has 20).']
    15611726
     1727Custom error messages
     1728>>> e = {'required': 'REQUIRED'}
     1729>>> e['invalid'] = 'INVALID'
     1730>>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s'
     1731>>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s'
     1732>>> f = EmailField(min_length=8, max_length=10, error_messages=e)
     1733>>> f.clean('')
     1734Traceback (most recent call last):
     1735...
     1736ValidationError: [u'REQUIRED']
     1737>>> f.clean('abcdefgh')
     1738Traceback (most recent call last):
     1739...
     1740ValidationError: [u'INVALID']
     1741>>> f.clean('a@b.com')
     1742Traceback (most recent call last):
     1743...
     1744ValidationError: [u'LENGTH 7, MIN LENGTH 8']
     1745>>> f.clean('aye@bee.com')
     1746Traceback (most recent call last):
     1747...
     1748ValidationError: [u'LENGTH 11, MAX LENGTH 10']
     1749
    15621750# FileField ##################################################################
    15631751
    15641752>>> f = FileField()
     
    15951783>>> type(f.clean({'filename': 'name', 'content':'Some File Content'}))
    15961784<class 'django.newforms.fields.UploadedFile'>
    15971785
     1786Custom error messages
     1787>>> e = {'required': 'REQUIRED'}
     1788>>> e['invalid'] = 'INVALID'
     1789>>> e['missing'] = 'MISSING'
     1790>>> e['empty'] = 'EMPTY FILE'
     1791>>> f = FileField(error_messages=e)
     1792>>> f.clean('')
     1793Traceback (most recent call last):
     1794...
     1795ValidationError: [u'REQUIRED']
     1796>>> f.clean('abc')
     1797Traceback (most recent call last):
     1798...
     1799ValidationError: [u'INVALID']
     1800>>> f.clean({})
     1801Traceback (most recent call last):
     1802...
     1803ValidationError: [u'MISSING']
     1804>>> f.clean({'filename': 'name', 'content':''})
     1805Traceback (most recent call last):
     1806...
     1807ValidationError: [u'EMPTY FILE']
     1808
    15981809# URLField ##################################################################
    15991810
    16001811>>> f = URLField()
     
    17051916...
    17061917ValidationError: [u'Ensure this value has at most 20 characters (it has 37).']
    17071918
     1919Custom error messages
     1920>>> e = {'required': 'REQUIRED'}
     1921>>> e['invalid'] = 'INVALID'
     1922>>> e['invalid_link'] = 'INVALID LINK'
     1923>>> f = URLField(verify_exists=True, error_messages=e)
     1924>>> f.clean('')
     1925Traceback (most recent call last):
     1926...
     1927ValidationError: [u'REQUIRED']
     1928>>> f.clean('abc.c')
     1929Traceback (most recent call last):
     1930...
     1931ValidationError: [u'INVALID']
     1932>>> f.clean('http://www.jfoiwjfoi23jfoijoaijfoiwjofiwjefewl.com')
     1933Traceback (most recent call last):
     1934...
     1935ValidationError: [u'INVALID LINK']
     1936
    17081937# BooleanField ################################################################
    17091938
    17101939>>> f = BooleanField()
     
    17431972>>> f.clean('Django rocks')
    17441973True
    17451974
     1975Custom error messages
     1976>>> e = {'required': 'REQUIRED'}
     1977>>> f = BooleanField(error_messages=e)
     1978>>> f.clean('')
     1979Traceback (most recent call last):
     1980...
     1981ValidationError: [u'REQUIRED']
     1982
    17461983# ChoiceField #################################################################
    17471984
    17481985>>> f = ChoiceField(choices=[('1', '1'), ('2', '2')])
     
    17852022...
    17862023ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
    17872024
     2025Custom error messages
     2026>>> e = {'required': 'REQUIRED'}
     2027>>> e['invalid_choice'] = '%(value)s IS INVALID CHOICE'
     2028>>> f = ChoiceField(choices=[('a', 'aye')], error_messages=e)
     2029>>> f.clean('')
     2030Traceback (most recent call last):
     2031...
     2032ValidationError: [u'REQUIRED']
     2033>>> f.clean('b')
     2034Traceback (most recent call last):
     2035...
     2036ValidationError: [u'b IS INVALID CHOICE']
     2037
    17882038# NullBooleanField ############################################################
    17892039
    17902040>>> f = NullBooleanField()
     
    18652115...
    18662116ValidationError: [u'Select a valid choice. 3 is not one of the available choices.']
    18672117
     2118Custom error messages
     2119>>> e = {'required': 'REQUIRED'}
     2120>>> e['invalid_choice'] = '%(value)s IS INVALID CHOICE'
     2121>>> e['invalid_list'] = 'NOT A LIST'
     2122>>> f = MultipleChoiceField(choices=[('a', 'aye')], error_messages=e)
     2123>>> f.clean('')
     2124Traceback (most recent call last):
     2125...
     2126ValidationError: [u'REQUIRED']
     2127>>> f.clean('b')
     2128Traceback (most recent call last):
     2129...
     2130ValidationError: [u'NOT A LIST']
     2131>>> f.clean(['b'])
     2132Traceback (most recent call last):
     2133...
     2134ValidationError: [u'b IS INVALID CHOICE']
     2135
    18682136# ComboField ##################################################################
    18692137
    18702138ComboField takes a list of fields that should be used to validate a value,
     
    19732241...
    19742242ValidationError: [u'Enter a valid date.']
    19752243
     2244Custom error messages
     2245>>> e = {'required': 'REQUIRED'}
     2246>>> e['invalid_date'] = 'INVALID DATE'
     2247>>> e['invalid_time'] = 'INVALID TIME'
     2248>>> f = SplitDateTimeField(error_messages=e)
     2249>>> f.clean('')
     2250Traceback (most recent call last):
     2251...
     2252ValidationError: [u'REQUIRED']
     2253>>> f.clean(['a', 'b'])
     2254Traceback (most recent call last):
     2255...
     2256ValidationError: [u'INVALID DATE', u'INVALID TIME']
     2257
    19762258#########
    19772259# Forms #
    19782260#########
     
    38214103True
    38224104>>> f.cleaned_data['username']
    38234105u'sirrobin'
     4106
    38244107"""
    38254108
    38264109__test__ = {
Back to Top