12 from cStringIO
import StringIO
14 from StringIO
import StringIO
16 # Python 2.3 fallbacks
18 from decimal
import Decimal
, DecimalException
20 from django
.utils
._decimal
import Decimal
, DecimalException
24 from sets
import Set
as set
26 import django
.core
.exceptions
27 from django
.utils
.translation
import ugettext_lazy
as _
28 from django
.utils
.encoding
import smart_unicode
, smart_str
30 from util
import ErrorList
, ValidationError
31 from widgets
import TextInput
, PasswordInput
, HiddenInput
, MultipleHiddenInput
, FileInput
, CheckboxInput
, Select
, NullBooleanSelect
, SelectMultiple
, DateTimeInput
, TimeInput
, SplitHiddenDateTimeWidget
32 from django
.core
.files
.uploadedfile
import SimpleUploadedFile
as UploadedFile
35 'Field', 'CharField', 'IntegerField',
36 'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
37 'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
38 'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField', 'TimeField',
39 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField',
40 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
41 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
42 'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField',
46 # These values, if given to to_python(), will trigger the self.required check.
47 EMPTY_VALUES
= (None, '')
51 widget
= TextInput
# Default widget to use when rendering this type of Field.
52 hidden_widget
= HiddenInput
# Default widget to use when rendering this as "hidden".
53 default_error_messages
= {
54 'required': _(u
'This field is required.'),
55 'invalid': _(u
'Enter a valid value.'),
58 # Tracks each time a Field instance is created. Used to retain order.
61 def __init__(self
, required
=True, widget
=None, label
=None, initial
=None,
62 help_text
=None, error_messages
=None, show_hidden_initial
=False):
63 # required -- Boolean that specifies whether the field is required.
65 # widget -- A Widget class, or instance of a Widget class, that should
66 # be used for this Field when displaying it. Each Field has a
67 # default Widget that it'll use if you don't specify this. In
68 # most cases, the default widget is TextInput.
69 # label -- A verbose name for this field, for use in displaying this
70 # field in a form. By default, Django will use a "pretty"
71 # version of the form field name, if the Field is part of a
73 # initial -- A value to use in this Field's initial display. This value
74 # is *not* used as a fallback if data isn't given.
75 # help_text -- An optional string to use as "help text" for this Field.
76 # show_hidden_initial -- Boolean that specifies if it is needed to render a
77 # hidden widget with initial value after widget.
79 label
= smart_unicode(label
)
80 self
.required
, self
.label
, self
.initial
= required
, label
, initial
81 self
.show_hidden_initial
= show_hidden_initial
85 self
.help_text
= smart_unicode(help_text
)
86 widget
= widget
or self
.widget
87 if isinstance(widget
, type):
90 # Hook into self.widget_attrs() for any Field-specific HTML attributes.
91 extra_attrs
= self
.widget_attrs(widget
)
93 widget
.attrs
.update(extra_attrs
)
97 # Increase the creation counter, and save our local copy.
98 self
.creation_counter
= Field
.creation_counter
99 Field
.creation_counter
+= 1
101 def set_class_error_messages(messages
, klass
):
102 for base_class
in klass
.__bases
__:
103 set_class_error_messages(messages
, base_class
)
104 messages
.update(getattr(klass
, 'default_error_messages', {}))
107 set_class_error_messages(messages
, self
.__class
__)
108 messages
.update(error_messages
or {})
109 self
.error_messages
= messages
111 def clean(self
, value
):
113 Validates the given value and returns its "cleaned" value as an
114 appropriate Python object.
116 Raises ValidationError for any errors.
118 if self
.required
and value
in EMPTY_VALUES
:
119 raise ValidationError(self
.error_messages
['required'])
122 def widget_attrs(self
, widget
):
124 Given a Widget instance (*not* a Widget class), returns a dictionary of
125 any HTML attributes that should be added to the Widget, based on this
130 def __deepcopy__(self
, memo
):
131 result
= copy
.copy(self
)
132 memo
[id(self
)] = result
133 result
.widget
= copy
.deepcopy(self
.widget
, memo
)
136 class CharField(Field
):
137 default_error_messages
= {
138 'max_length': _(u
'Ensure this value has at most %(max)d characters (it has %(length)d).'),
139 'min_length': _(u
'Ensure this value has at least %(min)d characters (it has %(length)d).'),
142 def __init__(self
, max_length
=None, min_length
=None, *args
, **kwargs
):
143 self
.max_length
, self
.min_length
= max_length
, min_length
144 super(CharField
, self
).__init
__(*args
, **kwargs
)
146 def clean(self
, value
):
147 "Validates max_length and min_length. Returns a Unicode object."
148 super(CharField
, self
).clean(value
)
149 if value
in EMPTY_VALUES
:
151 value
= smart_unicode(value
)
152 value_length
= len(value
)
153 if self
.max_length
is not None and value_length
> self
.max_length
:
154 raise ValidationError(self
.error_messages
['max_length'] % {'max': self
.max_length
, 'length': value_length
})
155 if self
.min_length
is not None and value_length
< self
.min_length
:
156 raise ValidationError(self
.error_messages
['min_length'] % {'min': self
.min_length
, 'length': value_length
})
159 def widget_attrs(self
, widget
):
160 if self
.max_length
is not None and isinstance(widget
, (TextInput
, PasswordInput
)):
161 # The HTML attribute is maxlength, not max_length.
162 return {'maxlength': str(self
.max_length
)}
164 class IntegerField(Field
):
165 default_error_messages
= {
166 'invalid': _(u
'Enter a whole number.'),
167 'max_value': _(u
'Ensure this value is less than or equal to %s.'),
168 'min_value': _(u
'Ensure this value is greater than or equal to %s.'),
171 def __init__(self
, max_value
=None, min_value
=None, *args
, **kwargs
):
172 self
.max_value
, self
.min_value
= max_value
, min_value
173 super(IntegerField
, self
).__init
__(*args
, **kwargs
)
175 def clean(self
, value
):
177 Validates that int() can be called on the input. Returns the result
178 of int(). Returns None for empty values.
180 super(IntegerField
, self
).clean(value
)
181 if value
in EMPTY_VALUES
:
184 value
= int(str(value
))
185 except (ValueError, TypeError):
186 raise ValidationError(self
.error_messages
['invalid'])
187 if self
.max_value
is not None and value
> self
.max_value
:
188 raise ValidationError(self
.error_messages
['max_value'] % self
.max_value
)
189 if self
.min_value
is not None and value
< self
.min_value
:
190 raise ValidationError(self
.error_messages
['min_value'] % self
.min_value
)
193 class FloatField(Field
):
194 default_error_messages
= {
195 'invalid': _(u
'Enter a number.'),
196 'max_value': _(u
'Ensure this value is less than or equal to %s.'),
197 'min_value': _(u
'Ensure this value is greater than or equal to %s.'),
200 def __init__(self
, max_value
=None, min_value
=None, *args
, **kwargs
):
201 self
.max_value
, self
.min_value
= max_value
, min_value
202 Field
.__init
__(self
, *args
, **kwargs
)
204 def clean(self
, value
):
206 Validates that float() can be called on the input. Returns a float.
207 Returns None for empty values.
209 super(FloatField
, self
).clean(value
)
210 if not self
.required
and value
in EMPTY_VALUES
:
214 except (ValueError, TypeError):
215 raise ValidationError(self
.error_messages
['invalid'])
216 if self
.max_value
is not None and value
> self
.max_value
:
217 raise ValidationError(self
.error_messages
['max_value'] % self
.max_value
)
218 if self
.min_value
is not None and value
< self
.min_value
:
219 raise ValidationError(self
.error_messages
['min_value'] % self
.min_value
)
222 class DecimalField(Field
):
223 default_error_messages
= {
224 'invalid': _(u
'Enter a number.'),
225 'max_value': _(u
'Ensure this value is less than or equal to %s.'),
226 'min_value': _(u
'Ensure this value is greater than or equal to %s.'),
227 'max_digits': _('Ensure that there are no more than %s digits in total.'),
228 'max_decimal_places': _('Ensure that there are no more than %s decimal places.'),
229 'max_whole_digits': _('Ensure that there are no more than %s digits before the decimal point.')
232 def __init__(self
, max_value
=None, min_value
=None, max_digits
=None, decimal_places
=None, *args
, **kwargs
):
233 self
.max_value
, self
.min_value
= max_value
, min_value
234 self
.max_digits
, self
.decimal_places
= max_digits
, decimal_places
235 Field
.__init
__(self
, *args
, **kwargs
)
237 def clean(self
, value
):
239 Validates that the input is a decimal number. Returns a Decimal
240 instance. Returns None for empty values. Ensures that there are no more
241 than max_digits in the number, and no more than decimal_places digits
242 after the decimal point.
244 super(DecimalField
, self
).clean(value
)
245 if not self
.required
and value
in EMPTY_VALUES
:
247 value
= smart_str(value
).strip()
249 value
= Decimal(value
)
250 except DecimalException
:
251 raise ValidationError(self
.error_messages
['invalid'])
253 sign
, digittuple
, exponent
= value
.as_tuple()
254 decimals
= abs(exponent
)
255 # digittuple doesn't include any leading zeros.
256 digits
= len(digittuple
)
257 if decimals
>= digits
:
258 # We have leading zeros up to or past the decimal point. Count
259 # everything past the decimal point as a digit. We also add one
260 # for leading zeros before the decimal point (any number of leading
261 # whole zeros collapse to one digit).
262 digits
= decimals
+ 1
263 whole_digits
= digits
- decimals
265 if self
.max_value
is not None and value
> self
.max_value
:
266 raise ValidationError(self
.error_messages
['max_value'] % self
.max_value
)
267 if self
.min_value
is not None and value
< self
.min_value
:
268 raise ValidationError(self
.error_messages
['min_value'] % self
.min_value
)
269 if self
.max_digits
is not None and digits
> self
.max_digits
:
270 raise ValidationError(self
.error_messages
['max_digits'] % self
.max_digits
)
271 if self
.decimal_places
is not None and decimals
> self
.decimal_places
:
272 raise ValidationError(self
.error_messages
['max_decimal_places'] % self
.decimal_places
)
273 if self
.max_digits
is not None and self
.decimal_places
is not None and whole_digits
> (self
.max_digits
- self
.decimal_places
):
274 raise ValidationError(self
.error_messages
['max_whole_digits'] % (self
.max_digits
- self
.decimal_places
))
277 DEFAULT_DATE_INPUT_FORMATS
= (
278 '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
279 '%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006'
280 '%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006'
281 '%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006'
282 '%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006'
285 class DateField(Field
):
286 default_error_messages
= {
287 'invalid': _(u
'Enter a valid date.'),
290 def __init__(self
, input_formats
=None, *args
, **kwargs
):
291 super(DateField
, self
).__init
__(*args
, **kwargs
)
292 self
.input_formats
= input_formats
or DEFAULT_DATE_INPUT_FORMATS
294 def clean(self
, value
):
296 Validates that the input can be converted to a date. Returns a Python
297 datetime.date object.
299 super(DateField
, self
).clean(value
)
300 if value
in EMPTY_VALUES
:
302 if isinstance(value
, datetime
.datetime
):
304 if isinstance(value
, datetime
.date
):
306 for format
in self
.input_formats
:
308 return datetime
.date(*time
.strptime(value
, format
)[:3])
311 raise ValidationError(self
.error_messages
['invalid'])
313 DEFAULT_TIME_INPUT_FORMATS
= (
314 '%H:%M:%S', # '14:30:59'
318 class TimeField(Field
):
320 default_error_messages
= {
321 'invalid': _(u
'Enter a valid time.')
324 def __init__(self
, input_formats
=None, *args
, **kwargs
):
325 super(TimeField
, self
).__init
__(*args
, **kwargs
)
326 self
.input_formats
= input_formats
or DEFAULT_TIME_INPUT_FORMATS
328 def clean(self
, value
):
330 Validates that the input can be converted to a time. Returns a Python
331 datetime.time object.
333 super(TimeField
, self
).clean(value
)
334 if value
in EMPTY_VALUES
:
336 if isinstance(value
, datetime
.time
):
338 for format
in self
.input_formats
:
340 return datetime
.time(*time
.strptime(value
, format
)[3:6])
343 raise ValidationError(self
.error_messages
['invalid'])
345 DEFAULT_DATETIME_INPUT_FORMATS
= (
346 '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
347 '%Y-%m-%d %H:%M', # '2006-10-25 14:30'
348 '%Y-%m-%d', # '2006-10-25'
349 '%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59'
350 '%m/%d/%Y %H:%M', # '10/25/2006 14:30'
351 '%m/%d/%Y', # '10/25/2006'
352 '%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59'
353 '%m/%d/%y %H:%M', # '10/25/06 14:30'
354 '%m/%d/%y', # '10/25/06'
357 class DateTimeField(Field
):
358 widget
= DateTimeInput
359 default_error_messages
= {
360 'invalid': _(u
'Enter a valid date/time.'),
363 def __init__(self
, input_formats
=None, *args
, **kwargs
):
364 super(DateTimeField
, self
).__init
__(*args
, **kwargs
)
365 self
.input_formats
= input_formats
or DEFAULT_DATETIME_INPUT_FORMATS
367 def clean(self
, value
):
369 Validates that the input can be converted to a datetime. Returns a
370 Python datetime.datetime object.
372 super(DateTimeField
, self
).clean(value
)
373 if value
in EMPTY_VALUES
:
375 if isinstance(value
, datetime
.datetime
):
377 if isinstance(value
, datetime
.date
):
378 return datetime
.datetime(value
.year
, value
.month
, value
.day
)
379 if isinstance(value
, list):
380 # Input comes from a SplitDateTimeWidget, for example. So, it's two
381 # components: date and time.
383 raise ValidationError(self
.error_messages
['invalid'])
384 value
= '%s %s' % tuple(value
)
385 for format
in self
.input_formats
:
387 return datetime
.datetime(*time
.strptime(value
, format
)[:6])
390 raise ValidationError(self
.error_messages
['invalid'])
392 class RegexField(CharField
):
393 def __init__(self
, regex
, max_length
=None, min_length
=None, error_message
=None, *args
, **kwargs
):
395 regex can be either a string or a compiled regular expression object.
396 error_message is an optional error message to use, if
397 'Enter a valid value' is too generic for you.
399 # error_message is just kept for backwards compatibility:
401 error_messages
= kwargs
.get('error_messages') or {}
402 error_messages
['invalid'] = error_message
403 kwargs
['error_messages'] = error_messages
404 super(RegexField
, self
).__init
__(max_length
, min_length
, *args
, **kwargs
)
405 if isinstance(regex
, basestring
):
406 regex
= re
.compile(regex
)
409 def clean(self
, value
):
411 Validates that the input matches the regular expression. Returns a
414 value
= super(RegexField
, self
).clean(value
)
417 if not self
.regex
.search(value
):
418 raise ValidationError(self
.error_messages
['invalid'])
421 email_re
= re
.compile(
422 r
"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
423 r
'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string
424 r
')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re
.IGNORECASE
) # domain
426 class EmailField(RegexField
):
427 default_error_messages
= {
428 'invalid': _(u
'Enter a valid e-mail address.'),
431 def __init__(self
, max_length
=None, min_length
=None, *args
, **kwargs
):
432 RegexField
.__init
__(self
, email_re
, max_length
, min_length
, *args
,
436 from django
.conf
import settings
437 URL_VALIDATOR_USER_AGENT
= settings
.URL_VALIDATOR_USER_AGENT
439 # It's OK if Django settings aren't configured.
440 URL_VALIDATOR_USER_AGENT
= 'Django (http://www.djangoproject.com/)'
443 class FileField(Field
):
445 default_error_messages
= {
446 'invalid': _(u
"No file was submitted. Check the encoding type on the form."),
447 'missing': _(u
"No file was submitted."),
448 'empty': _(u
"The submitted file is empty."),
451 def __init__(self
, *args
, **kwargs
):
452 super(FileField
, self
).__init
__(*args
, **kwargs
)
454 def clean(self
, data
, initial
=None):
455 super(FileField
, self
).clean(initial
or data
)
456 if not self
.required
and data
in EMPTY_VALUES
:
458 elif not data
and initial
:
461 # UploadedFile objects should have name and size attributes.
463 file_name
= data
.name
464 file_size
= data
.size
465 except AttributeError:
466 raise ValidationError(self
.error_messages
['invalid'])
469 raise ValidationError(self
.error_messages
['invalid'])
471 raise ValidationError(self
.error_messages
['empty'])
475 class ImageField(FileField
):
476 default_error_messages
= {
477 'invalid_image': _(u
"Upload a valid image. The file you uploaded was either not an image or a corrupted image."),
480 def clean(self
, data
, initial
=None):
482 Checks that the file-upload field data contains a valid image (GIF, JPG,
483 PNG, possibly others -- whatever the Python Imaging Library supports).
485 f
= super(ImageField
, self
).clean(data
, initial
)
488 elif not data
and initial
:
490 from PIL
import Image
492 # We need to get a file object for PIL. We might have a path or we might
493 # have to read the data into memory.
494 if hasattr(data
, 'temporary_file_path'):
495 file = data
.temporary_file_path()
497 if hasattr(data
, 'read'):
498 file = StringIO(data
.read())
500 file = StringIO(data
['content'])
503 # load() is the only method that can spot a truncated JPEG,
504 # but it cannot be called sanely after verify()
505 trial_image
= Image
.open(file)
508 # Since we're about to use the file again we have to reset the
509 # file object if possible.
510 if hasattr(file, 'reset'):
513 # verify() is the only method that can spot a corrupt PNG,
514 # but it must be called immediately after the constructor
515 trial_image
= Image
.open(file)
518 # Under PyPy, it is possible to import PIL. However, the underlying
519 # _imaging C module isn't available, so an ImportError will be
520 # raised. Catch and re-raise.
522 except Exception: # Python Imaging Library doesn't recognize it as an image
523 raise ValidationError(self
.error_messages
['invalid_image'])
524 if hasattr(f
, 'seek') and callable(f
.seek
):
529 r
'^https?://' # http:// or https://
530 r
'(?:(?:[A-Z0-9-]+\.)+[A-Z]{2,6}|' #domain...
531 r
'localhost|' #localhost...
532 r
'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
533 r
'(?::\d+)?' # optional port
534 r
'(?:/?|/\S+)$', re
.IGNORECASE
)
536 class URLField(RegexField
):
537 default_error_messages
= {
538 'invalid': _(u
'Enter a valid URL.'),
539 'invalid_link': _(u
'This URL appears to be a broken link.'),
542 def __init__(self
, max_length
=None, min_length
=None, verify_exists
=False,
543 validator_user_agent
=URL_VALIDATOR_USER_AGENT
, *args
, **kwargs
):
544 super(URLField
, self
).__init
__(url_re
, max_length
, min_length
, *args
,
546 self
.verify_exists
= verify_exists
547 self
.user_agent
= validator_user_agent
549 def clean(self
, value
):
550 # If no URL scheme given, assume http://
551 if value
and '://' not in value
:
552 value
= u
'http://%s' % value
553 # If no URL path given, assume /
554 if value
and not urlparse
.urlsplit(value
)[2]:
556 value
= super(URLField
, self
).clean(value
)
559 if self
.verify_exists
:
562 "Accept": "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
563 "Accept-Language": "en-us,en;q=0.5",
564 "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
565 "Connection": "close",
566 "User-Agent": self
.user_agent
,
569 req
= urllib2
.Request(value
, None, headers
)
570 u
= urllib2
.urlopen(req
)
572 raise ValidationError(self
.error_messages
['invalid'])
573 except: # urllib2.URLError, httplib.InvalidURL, etc.
574 raise ValidationError(self
.error_messages
['invalid_link'])
577 class BooleanField(Field
):
578 widget
= CheckboxInput
580 def clean(self
, value
):
581 """Returns a Python boolean object."""
582 # Explicitly check for the string 'False', which is what a hidden field
583 # will submit for False. Because bool("True") == True, we don't need to
584 # handle that explicitly.
589 super(BooleanField
, self
).clean(value
)
590 if not value
and self
.required
:
591 raise ValidationError(self
.error_messages
['required'])
594 class NullBooleanField(BooleanField
):
596 A field whose valid values are None, True and False. Invalid values are
599 widget
= NullBooleanSelect
601 def clean(self
, value
):
603 Explicitly checks for the string 'True' and 'False', which is what a
604 hidden field will submit for True and False. Unlike the
605 Booleanfield we also need to check for True, because we are not using
608 if value
in (True, 'True'):
610 elif value
in (False, 'False'):
615 class ChoiceField(Field
):
617 default_error_messages
= {
618 'invalid_choice': _(u
'Select a valid choice. %(value)s is not one of the available choices.'),
621 def __init__(self
, choices
=(), required
=True, widget
=None, label
=None,
622 initial
=None, help_text
=None, *args
, **kwargs
):
623 super(ChoiceField
, self
).__init
__(required
, widget
, label
, initial
,
624 help_text
, *args
, **kwargs
)
625 self
.choices
= choices
627 def _get_choices(self
):
630 def _set_choices(self
, value
):
631 # Setting choices also sets the choices on the widget.
632 # choices can be any iterable, but we call list() on it because
633 # it will be consumed more than once.
634 self
._choices
= self
.widget
.choices
= list(value
)
636 choices
= property(_get_choices
, _set_choices
)
638 def clean(self
, value
):
640 Validates that the input is in self.choices.
642 value
= super(ChoiceField
, self
).clean(value
)
643 if value
in EMPTY_VALUES
:
645 value
= smart_unicode(value
)
648 if not self
.valid_value(value
):
649 raise ValidationError(self
.error_messages
['invalid_choice'] % {'value': value
})
652 def valid_value(self
, value
):
653 "Check to see if the provided value is a valid choice"
654 for k
, v
in self
.choices
:
655 if type(v
) in (tuple, list):
656 # This is an optgroup, so look inside the group for options
658 if value
== smart_unicode(k2
):
661 if value
== smart_unicode(k
):
665 class TypedChoiceField(ChoiceField
):
666 def __init__(self
, *args
, **kwargs
):
667 self
.coerce = kwargs
.pop('coerce', lambda val
: val
)
668 self
.empty_value
= kwargs
.pop('empty_value', '')
669 super(TypedChoiceField
, self
).__init
__(*args
, **kwargs
)
671 def clean(self
, value
):
673 Validate that the value is in self.choices and can be coerced to the
676 value
= super(TypedChoiceField
, self
).clean(value
)
677 if value
== self
.empty_value
or value
in EMPTY_VALUES
:
678 return self
.empty_value
680 # Hack alert: This field is purpose-made to use with Field.to_python as
681 # a coercion function so that ModelForms with choices work. However,
682 # Django's Field.to_python raises django.core.exceptions.ValidationError,
683 # which is a *different* exception than
684 # django.forms.utils.ValidationError. So unfortunatly we need to catch
687 value
= self
.coerce(value
)
688 except (ValueError, TypeError, django
.core
.exceptions
.ValidationError
):
689 raise ValidationError(self
.error_messages
['invalid_choice'] % {'value': value
})
692 class MultipleChoiceField(ChoiceField
):
693 hidden_widget
= MultipleHiddenInput
694 widget
= SelectMultiple
695 default_error_messages
= {
696 'invalid_choice': _(u
'Select a valid choice. %(value)s is not one of the available choices.'),
697 'invalid_list': _(u
'Enter a list of values.'),
700 def clean(self
, value
):
702 Validates that the input is a list or tuple.
704 if self
.required
and not value
:
705 raise ValidationError(self
.error_messages
['required'])
706 elif not self
.required
and not value
:
708 if not isinstance(value
, (list, tuple)):
709 raise ValidationError(self
.error_messages
['invalid_list'])
710 new_value
= [smart_unicode(val
) for val
in value
]
711 # Validate that each value in the value list is in self.choices.
712 for val
in new_value
:
713 if not self
.valid_value(val
):
714 raise ValidationError(self
.error_messages
['invalid_choice'] % {'value': val
})
717 class ComboField(Field
):
719 A Field whose clean() method calls multiple Field clean() methods.
721 def __init__(self
, fields
=(), *args
, **kwargs
):
722 super(ComboField
, self
).__init
__(*args
, **kwargs
)
723 # Set 'required' to False on the individual fields, because the
724 # required validation will be handled by ComboField, not by those
730 def clean(self
, value
):
732 Validates the given value against all of self.fields, which is a
733 list of Field instances.
735 super(ComboField
, self
).clean(value
)
736 for field
in self
.fields
:
737 value
= field
.clean(value
)
740 class MultiValueField(Field
):
742 A Field that aggregates the logic of multiple Fields.
744 Its clean() method takes a "decompressed" list of values, which are then
745 cleaned into a single value according to self.fields. Each value in
746 this list is cleaned by the corresponding field -- the first value is
747 cleaned by the first field, the second value is cleaned by the second
748 field, etc. Once all fields are cleaned, the list of clean values is
749 "compressed" into a single value.
751 Subclasses should not have to implement clean(). Instead, they must
752 implement compress(), which takes a list of valid values and returns a
753 "compressed" version of those values -- a single value.
755 You'll probably want to use this with MultiWidget.
757 default_error_messages
= {
758 'invalid': _(u
'Enter a list of values.'),
761 def __init__(self
, fields
=(), *args
, **kwargs
):
762 super(MultiValueField
, self
).__init
__(*args
, **kwargs
)
763 # Set 'required' to False on the individual fields, because the
764 # required validation will be handled by MultiValueField, not by those
770 def clean(self
, value
):
772 Validates every value in the given list. A value is validated against
773 the corresponding Field in self.fields.
775 For example, if this MultiValueField was instantiated with
776 fields=(DateField(), TimeField()), clean() would call
777 DateField.clean(value[0]) and TimeField.clean(value[1]).
781 if not value
or isinstance(value
, (list, tuple)):
782 if not value
or not [v
for v
in value
if v
not in EMPTY_VALUES
]:
784 raise ValidationError(self
.error_messages
['required'])
786 return self
.compress([])
788 raise ValidationError(self
.error_messages
['invalid'])
789 for i
, field
in enumerate(self
.fields
):
791 field_value
= value
[i
]
794 if self
.required
and field_value
in EMPTY_VALUES
:
795 raise ValidationError(self
.error_messages
['required'])
797 clean_data
.append(field
.clean(field_value
))
798 except ValidationError
, e
:
799 # Collect all validation errors in a single list, which we'll
800 # raise at the end of clean(), rather than raising a single
801 # exception for the first error we encounter.
802 errors
.extend(e
.messages
)
804 raise ValidationError(errors
)
805 return self
.compress(clean_data
)
807 def compress(self
, data_list
):
809 Returns a single value for the given list of values. The values can be
812 For example, if this MultiValueField was instantiated with
813 fields=(DateField(), TimeField()), this might return a datetime
814 object created by combining the date and time in data_list.
816 raise NotImplementedError('Subclasses must implement this method.')
818 class FilePathField(ChoiceField
):
819 def __init__(self
, path
, match
=None, recursive
=False, required
=True,
820 widget
=None, label
=None, initial
=None, help_text
=None,
822 self
.path
, self
.match
, self
.recursive
= path
, match
, recursive
823 super(FilePathField
, self
).__init
__(choices
=(), required
=required
,
824 widget
=widget
, label
=label
, initial
=initial
, help_text
=help_text
,
827 if self
.match
is not None:
828 self
.match_re
= re
.compile(self
.match
)
830 for root
, dirs
, files
in os
.walk(self
.path
):
832 if self
.match
is None or self
.match_re
.search(f
):
833 f
= os
.path
.join(root
, f
)
834 self
.choices
.append((f
, f
.replace(path
, "", 1)))
837 for f
in os
.listdir(self
.path
):
838 full_file
= os
.path
.join(self
.path
, f
)
839 if os
.path
.isfile(full_file
) and (self
.match
is None or self
.match_re
.search(f
)):
840 self
.choices
.append((full_file
, f
))
843 self
.widget
.choices
= self
.choices
845 class SplitDateTimeField(MultiValueField
):
846 hidden_widget
= SplitHiddenDateTimeWidget
847 default_error_messages
= {
848 'invalid_date': _(u
'Enter a valid date.'),
849 'invalid_time': _(u
'Enter a valid time.'),
852 def __init__(self
, *args
, **kwargs
):
853 errors
= self
.default_error_messages
.copy()
854 if 'error_messages' in kwargs
:
855 errors
.update(kwargs
['error_messages'])
857 DateField(error_messages
={'invalid': errors
['invalid_date']}),
858 TimeField(error_messages
={'invalid': errors
['invalid_time']}),
860 super(SplitDateTimeField
, self
).__init
__(fields
, *args
, **kwargs
)
862 def compress(self
, data_list
):
864 # Raise a validation error if time or date is empty
865 # (possible if SplitDateTimeField has required=False).
866 if data_list
[0] in EMPTY_VALUES
:
867 raise ValidationError(self
.error_messages
['invalid_date'])
868 if data_list
[1] in EMPTY_VALUES
:
869 raise ValidationError(self
.error_messages
['invalid_time'])
870 return datetime
.datetime
.combine(*data_list
)
873 ipv4_re
= re
.compile(r
'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
875 class IPAddressField(RegexField
):
876 default_error_messages
= {
877 'invalid': _(u
'Enter a valid IPv4 address.'),
880 def __init__(self
, *args
, **kwargs
):
881 super(IPAddressField
, self
).__init
__(ipv4_re
, *args
, **kwargs
)
883 slug_re
= re
.compile(r
'^[-\w]+$')
885 class SlugField(RegexField
):
886 default_error_messages
= {
887 'invalid': _(u
"Enter a valid 'slug' consisting of letters, numbers,"
888 u
" underscores or hyphens."),
891 def __init__(self
, *args
, **kwargs
):
892 super(SlugField
, self
).__init
__(slug_re
, *args
, **kwargs
)