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