Added name column to all role lists.
[Melange.git] / app / django / forms / fields.py
blobb20beb939f19f78f3f31a094993d531670bef64b
1 """
2 Field classes.
3 """
5 import copy
6 import datetime
7 import os
8 import re
9 import time
10 import urlparse
11 try:
12 from cStringIO import StringIO
13 except ImportError:
14 from StringIO import StringIO
16 # Python 2.3 fallbacks
17 try:
18 from decimal import Decimal, DecimalException
19 except ImportError:
20 from django.utils._decimal import Decimal, DecimalException
21 try:
22 set
23 except NameError:
24 from sets import Set as set
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
34 __all__ = (
35 'Field', 'CharField', 'IntegerField',
36 'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
37 'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
38 'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField', 'TimeField',
39 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField',
40 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
41 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
42 'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField',
43 'TypedChoiceField'
46 # These values, if given to to_python(), will trigger the self.required check.
47 EMPTY_VALUES = (None, '')
50 class Field(object):
51 widget = TextInput # Default widget to use when rendering this type of Field.
52 hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
53 default_error_messages = {
54 'required': _(u'This field is required.'),
55 'invalid': _(u'Enter a valid value.'),
58 # Tracks each time a Field instance is created. Used to retain order.
59 creation_counter = 0
61 def __init__(self, required=True, widget=None, label=None, initial=None,
62 help_text=None, error_messages=None, show_hidden_initial=False):
63 # required -- Boolean that specifies whether the field is required.
64 # True by default.
65 # widget -- A Widget class, or instance of a Widget class, that should
66 # be used for this Field when displaying it. Each Field has a
67 # default Widget that it'll use if you don't specify this. In
68 # most cases, the default widget is TextInput.
69 # label -- A verbose name for this field, for use in displaying this
70 # field in a form. By default, Django will use a "pretty"
71 # version of the form field name, if the Field is part of a
72 # Form.
73 # initial -- A value to use in this Field's initial display. This value
74 # is *not* used as a fallback if data isn't given.
75 # help_text -- An optional string to use as "help text" for this Field.
76 # show_hidden_initial -- Boolean that specifies if it is needed to render a
77 # hidden widget with initial value after widget.
78 if label is not None:
79 label = smart_unicode(label)
80 self.required, self.label, self.initial = required, label, initial
81 self.show_hidden_initial = show_hidden_initial
82 if help_text is None:
83 self.help_text = u''
84 else:
85 self.help_text = smart_unicode(help_text)
86 widget = widget or self.widget
87 if isinstance(widget, type):
88 widget = widget()
90 # Hook into self.widget_attrs() for any Field-specific HTML attributes.
91 extra_attrs = self.widget_attrs(widget)
92 if extra_attrs:
93 widget.attrs.update(extra_attrs)
95 self.widget = widget
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', {}))
106 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'])
120 return value
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
126 Field.
128 return {}
130 def __deepcopy__(self, memo):
131 result = copy.copy(self)
132 memo[id(self)] = result
133 result.widget = copy.deepcopy(self.widget, memo)
134 return result
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:
150 return u''
151 value = smart_unicode(value)
152 value_length = len(value)
153 if self.max_length is not None and value_length > self.max_length:
154 raise ValidationError(self.error_messages['max_length'] % {'max': self.max_length, 'length': value_length})
155 if self.min_length is not None and value_length < self.min_length:
156 raise ValidationError(self.error_messages['min_length'] % {'min': self.min_length, 'length': value_length})
157 return value
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:
182 return None
183 try:
184 value = int(str(value))
185 except (ValueError, TypeError):
186 raise ValidationError(self.error_messages['invalid'])
187 if self.max_value is not None and value > self.max_value:
188 raise ValidationError(self.error_messages['max_value'] % self.max_value)
189 if self.min_value is not None and value < self.min_value:
190 raise ValidationError(self.error_messages['min_value'] % self.min_value)
191 return value
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:
211 return None
212 try:
213 value = float(value)
214 except (ValueError, TypeError):
215 raise ValidationError(self.error_messages['invalid'])
216 if self.max_value is not None and value > self.max_value:
217 raise ValidationError(self.error_messages['max_value'] % self.max_value)
218 if self.min_value is not None and value < self.min_value:
219 raise ValidationError(self.error_messages['min_value'] % self.min_value)
220 return value
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:
246 return None
247 value = smart_str(value).strip()
248 try:
249 value = Decimal(value)
250 except DecimalException:
251 raise ValidationError(self.error_messages['invalid'])
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))
275 return value
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:
301 return None
302 if isinstance(value, datetime.datetime):
303 return value.date()
304 if isinstance(value, datetime.date):
305 return value
306 for format in self.input_formats:
307 try:
308 return datetime.date(*time.strptime(value, format)[:3])
309 except ValueError:
310 continue
311 raise ValidationError(self.error_messages['invalid'])
313 DEFAULT_TIME_INPUT_FORMATS = (
314 '%H:%M:%S', # '14:30:59'
315 '%H:%M', # '14:30'
318 class TimeField(Field):
319 widget = TimeInput
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:
335 return None
336 if isinstance(value, datetime.time):
337 return value
338 for format in self.input_formats:
339 try:
340 return datetime.time(*time.strptime(value, format)[3:6])
341 except ValueError:
342 continue
343 raise ValidationError(self.error_messages['invalid'])
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:
374 return None
375 if isinstance(value, datetime.datetime):
376 return value
377 if isinstance(value, datetime.date):
378 return datetime.datetime(value.year, value.month, value.day)
379 if isinstance(value, list):
380 # Input comes from a SplitDateTimeWidget, for example. So, it's two
381 # components: date and time.
382 if len(value) != 2:
383 raise ValidationError(self.error_messages['invalid'])
384 value = '%s %s' % tuple(value)
385 for format in self.input_formats:
386 try:
387 return datetime.datetime(*time.strptime(value, format)[:6])
388 except ValueError:
389 continue
390 raise ValidationError(self.error_messages['invalid'])
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:
400 if error_message:
401 error_messages = kwargs.get('error_messages') or {}
402 error_messages['invalid'] = error_message
403 kwargs['error_messages'] = error_messages
404 super(RegexField, self).__init__(max_length, min_length, *args, **kwargs)
405 if isinstance(regex, basestring):
406 regex = re.compile(regex)
407 self.regex = regex
409 def clean(self, value):
411 Validates that the input matches the regular expression. Returns a
412 Unicode object.
414 value = super(RegexField, self).clean(value)
415 if value == u'':
416 return value
417 if not self.regex.search(value):
418 raise ValidationError(self.error_messages['invalid'])
419 return value
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,
433 **kwargs)
435 try:
436 from django.conf import settings
437 URL_VALIDATOR_USER_AGENT = settings.URL_VALIDATOR_USER_AGENT
438 except ImportError:
439 # It's OK if Django settings aren't configured.
440 URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
443 class FileField(Field):
444 widget = FileInput
445 default_error_messages = {
446 'invalid': _(u"No file was submitted. Check the encoding type on the form."),
447 'missing': _(u"No file was submitted."),
448 'empty': _(u"The submitted file is empty."),
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:
457 return None
458 elif not data and initial:
459 return initial
461 # UploadedFile objects should have name and size attributes.
462 try:
463 file_name = data.name
464 file_size = data.size
465 except AttributeError:
466 raise ValidationError(self.error_messages['invalid'])
468 if not file_name:
469 raise ValidationError(self.error_messages['invalid'])
470 if not file_size:
471 raise ValidationError(self.error_messages['empty'])
473 return data
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)
486 if f is None:
487 return None
488 elif not data and initial:
489 return 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()
496 else:
497 if hasattr(data, 'read'):
498 file = StringIO(data.read())
499 else:
500 file = StringIO(data['content'])
502 try:
503 # load() is the only method that can spot a truncated JPEG,
504 # but it cannot be called sanely after verify()
505 trial_image = Image.open(file)
506 trial_image.load()
508 # Since we're about to use the file again we have to reset the
509 # file object if possible.
510 if hasattr(file, 'reset'):
511 file.reset()
513 # verify() is the only method that can spot a corrupt PNG,
514 # but it must be called immediately after the constructor
515 trial_image = Image.open(file)
516 trial_image.verify()
517 except ImportError:
518 # Under PyPy, it is possible to import PIL. However, the underlying
519 # _imaging C module isn't available, so an ImportError will be
520 # raised. Catch and re-raise.
521 raise
522 except Exception: # Python Imaging Library doesn't recognize it as an image
523 raise ValidationError(self.error_messages['invalid_image'])
524 if hasattr(f, 'seek') and callable(f.seek):
525 f.seek(0)
526 return f
528 url_re = re.compile(
529 r'^https?://' # http:// or https://
530 r'(?:(?:[A-Z0-9-]+\.)+[A-Z]{2,6}|' #domain...
531 r'localhost|' #localhost...
532 r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
533 r'(?::\d+)?' # optional port
534 r'(?:/?|/\S+)$', re.IGNORECASE)
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,
545 **kwargs)
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]:
555 value += '/'
556 value = super(URLField, self).clean(value)
557 if value == u'':
558 return value
559 if self.verify_exists:
560 import urllib2
561 headers = {
562 "Accept": "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
563 "Accept-Language": "en-us,en;q=0.5",
564 "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
565 "Connection": "close",
566 "User-Agent": self.user_agent,
568 try:
569 req = urllib2.Request(value, None, headers)
570 u = urllib2.urlopen(req)
571 except ValueError:
572 raise ValidationError(self.error_messages['invalid'])
573 except: # urllib2.URLError, httplib.InvalidURL, etc.
574 raise ValidationError(self.error_messages['invalid_link'])
575 return value
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.
585 if value == 'False':
586 value = False
587 else:
588 value = bool(value)
589 super(BooleanField, self).clean(value)
590 if not value and self.required:
591 raise ValidationError(self.error_messages['required'])
592 return value
594 class NullBooleanField(BooleanField):
596 A field whose valid values are None, True and False. Invalid values are
597 cleaned to None.
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
606 the bool() function
608 if value in (True, 'True'):
609 return True
610 elif value in (False, 'False'):
611 return False
612 else:
613 return None
615 class ChoiceField(Field):
616 widget = Select
617 default_error_messages = {
618 'invalid_choice': _(u'Select a valid choice. %(value)s is not one of the available choices.'),
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):
628 return self._choices
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:
644 value = u''
645 value = smart_unicode(value)
646 if value == u'':
647 return value
648 if not self.valid_value(value):
649 raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
650 return value
652 def valid_value(self, value):
653 "Check to see if the provided value is a valid choice"
654 for k, v in self.choices:
655 if type(v) in (tuple, list):
656 # This is an optgroup, so look inside the group for options
657 for k2, v2 in v:
658 if value == smart_unicode(k2):
659 return True
660 else:
661 if value == smart_unicode(k):
662 return True
663 return False
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
674 right type.
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
685 # both.
686 try:
687 value = self.coerce(value)
688 except (ValueError, TypeError, django.core.exceptions.ValidationError):
689 raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
690 return value
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:
707 return []
708 if not isinstance(value, (list, tuple)):
709 raise ValidationError(self.error_messages['invalid_list'])
710 new_value = [smart_unicode(val) for val in value]
711 # Validate that each value in the value list is in self.choices.
712 for val in new_value:
713 if not self.valid_value(val):
714 raise ValidationError(self.error_messages['invalid_choice'] % {'value': val})
715 return new_value
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
725 # individual fields.
726 for f in fields:
727 f.required = False
728 self.fields = fields
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)
738 return 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
765 # individual fields.
766 for f in fields:
767 f.required = False
768 self.fields = fields
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]).
779 clean_data = []
780 errors = ErrorList()
781 if not value or isinstance(value, (list, tuple)):
782 if not value or not [v for v in value if v not in EMPTY_VALUES]:
783 if self.required:
784 raise ValidationError(self.error_messages['required'])
785 else:
786 return self.compress([])
787 else:
788 raise ValidationError(self.error_messages['invalid'])
789 for i, field in enumerate(self.fields):
790 try:
791 field_value = value[i]
792 except IndexError:
793 field_value = None
794 if self.required and field_value in EMPTY_VALUES:
795 raise ValidationError(self.error_messages['required'])
796 try:
797 clean_data.append(field.clean(field_value))
798 except ValidationError, e:
799 # Collect all validation errors in a single list, which we'll
800 # raise at the end of clean(), rather than raising a single
801 # exception for the first error we encounter.
802 errors.extend(e.messages)
803 if errors:
804 raise ValidationError(errors)
805 return self.compress(clean_data)
807 def compress(self, data_list):
809 Returns a single value for the given list of values. The values can be
810 assumed to be valid.
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,
821 *args, **kwargs):
822 self.path, self.match, self.recursive = path, match, recursive
823 super(FilePathField, self).__init__(choices=(), required=required,
824 widget=widget, label=label, initial=initial, help_text=help_text,
825 *args, **kwargs)
826 self.choices = []
827 if self.match is not None:
828 self.match_re = re.compile(self.match)
829 if recursive:
830 for root, dirs, files in os.walk(self.path):
831 for f in files:
832 if self.match is None or self.match_re.search(f):
833 f = os.path.join(root, f)
834 self.choices.append((f, f.replace(path, "", 1)))
835 else:
836 try:
837 for f in os.listdir(self.path):
838 full_file = os.path.join(self.path, f)
839 if os.path.isfile(full_file) and (self.match is None or self.match_re.search(f)):
840 self.choices.append((full_file, f))
841 except OSError:
842 pass
843 self.widget.choices = self.choices
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'])
856 fields = (
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):
863 if data_list:
864 # Raise a validation error if time or date is empty
865 # (possible if SplitDateTimeField has required=False).
866 if data_list[0] in EMPTY_VALUES:
867 raise ValidationError(self.error_messages['invalid_date'])
868 if data_list[1] in EMPTY_VALUES:
869 raise ValidationError(self.error_messages['invalid_time'])
870 return datetime.datetime.combine(*data_list)
871 return None
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)