10 from django
.utils
.translation
import ugettext
11 from django
.utils
.encoding
import StrAndUnicode
, smart_unicode
13 from util
import ErrorList
, ValidationError
14 from widgets
import TextInput
, PasswordInput
, HiddenInput
, MultipleHiddenInput
, FileInput
, CheckboxInput
, Select
, NullBooleanSelect
, SelectMultiple
17 from decimal
import Decimal
, DecimalException
19 from django
.utils
._decimal
import Decimal
, DecimalException
22 'Field', 'CharField', 'IntegerField',
23 'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
24 'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
25 'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
26 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 'BooleanField',
27 'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
28 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
29 'SplitDateTimeField', 'IPAddressField',
32 # These values, if given to to_python(), will trigger the self.required check.
33 EMPTY_VALUES
= (None, '')
38 from sets
import Set
as set # Python 2.3 fallback
41 from decimal
import Decimal
43 from django
.utils
._decimal
import Decimal
# Python 2.3 fallback
46 widget
= TextInput
# Default widget to use when rendering this type of Field.
47 hidden_widget
= HiddenInput
# Default widget to use when rendering this as "hidden".
49 # Tracks each time a Field instance is created. Used to retain order.
52 def __init__(self
, required
=True, widget
=None, label
=None, initial
=None, help_text
=None):
53 # required -- Boolean that specifies whether the field is required.
55 # widget -- A Widget class, or instance of a Widget class, that should
56 # be used for this Field when displaying it. Each Field has a
57 # default Widget that it'll use if you don't specify this. In
58 # most cases, the default widget is TextInput.
59 # label -- A verbose name for this field, for use in displaying this
60 # field in a form. By default, Django will use a "pretty"
61 # version of the form field name, if the Field is part of a
63 # initial -- A value to use in this Field's initial display. This value
64 # is *not* used as a fallback if data isn't given.
65 # help_text -- An optional string to use as "help text" for this Field.
67 label
= smart_unicode(label
)
68 self
.required
, self
.label
, self
.initial
= required
, label
, initial
69 self
.help_text
= smart_unicode(help_text
or '')
70 widget
= widget
or self
.widget
71 if isinstance(widget
, type):
74 # Hook into self.widget_attrs() for any Field-specific HTML attributes.
75 extra_attrs
= self
.widget_attrs(widget
)
77 widget
.attrs
.update(extra_attrs
)
81 # Increase the creation counter, and save our local copy.
82 self
.creation_counter
= Field
.creation_counter
83 Field
.creation_counter
+= 1
85 def clean(self
, value
):
87 Validates the given value and returns its "cleaned" value as an
88 appropriate Python object.
90 Raises ValidationError for any errors.
92 if self
.required
and value
in EMPTY_VALUES
:
93 raise ValidationError(ugettext(u
'This field is required.'))
96 def widget_attrs(self
, widget
):
98 Given a Widget instance (*not* a Widget class), returns a dictionary of
99 any HTML attributes that should be added to the Widget, based on this
104 def __deepcopy__(self
, memo
):
105 result
= copy
.copy(self
)
106 memo
[id(self
)] = result
107 result
.widget
= copy
.deepcopy(self
.widget
, memo
)
110 class CharField(Field
):
111 def __init__(self
, max_length
=None, min_length
=None, *args
, **kwargs
):
112 self
.max_length
, self
.min_length
= max_length
, min_length
113 super(CharField
, self
).__init
__(*args
, **kwargs
)
115 def clean(self
, value
):
116 "Validates max_length and min_length. Returns a Unicode object."
117 super(CharField
, self
).clean(value
)
118 if value
in EMPTY_VALUES
:
120 value
= smart_unicode(value
)
121 value_length
= len(value
)
122 if self
.max_length
is not None and value_length
> self
.max_length
:
123 raise ValidationError(ugettext(u
'Ensure this value has at most %(max)d characters (it has %(length)d).') % {'max': self
.max_length
, 'length': value_length
})
124 if self
.min_length
is not None and value_length
< self
.min_length
:
125 raise ValidationError(ugettext(u
'Ensure this value has at least %(min)d characters (it has %(length)d).') % {'min': self
.min_length
, 'length': value_length
})
128 def widget_attrs(self
, widget
):
129 if self
.max_length
is not None and isinstance(widget
, (TextInput
, PasswordInput
)):
130 # The HTML attribute is maxlength, not max_length.
131 return {'maxlength': str(self
.max_length
)}
133 class IntegerField(Field
):
134 def __init__(self
, max_value
=None, min_value
=None, *args
, **kwargs
):
135 self
.max_value
, self
.min_value
= max_value
, min_value
136 super(IntegerField
, self
).__init
__(*args
, **kwargs
)
138 def clean(self
, value
):
140 Validates that int() can be called on the input. Returns the result
141 of int(). Returns None for empty values.
143 super(IntegerField
, self
).clean(value
)
144 if value
in EMPTY_VALUES
:
147 value
= int(str(value
))
148 except (ValueError, TypeError):
149 raise ValidationError(ugettext(u
'Enter a whole number.'))
150 if self
.max_value
is not None and value
> self
.max_value
:
151 raise ValidationError(ugettext(u
'Ensure this value is less than or equal to %s.') % self
.max_value
)
152 if self
.min_value
is not None and value
< self
.min_value
:
153 raise ValidationError(ugettext(u
'Ensure this value is greater than or equal to %s.') % self
.min_value
)
156 class FloatField(Field
):
157 def __init__(self
, max_value
=None, min_value
=None, *args
, **kwargs
):
158 self
.max_value
, self
.min_value
= max_value
, min_value
159 Field
.__init
__(self
, *args
, **kwargs
)
161 def clean(self
, value
):
163 Validates that float() can be called on the input. Returns a float.
164 Returns None for empty values.
166 super(FloatField
, self
).clean(value
)
167 if not self
.required
and value
in EMPTY_VALUES
:
171 except (ValueError, TypeError):
172 raise ValidationError(ugettext('Enter a number.'))
173 if self
.max_value
is not None and value
> self
.max_value
:
174 raise ValidationError(ugettext('Ensure this value is less than or equal to %s.') % self
.max_value
)
175 if self
.min_value
is not None and value
< self
.min_value
:
176 raise ValidationError(ugettext('Ensure this value is greater than or equal to %s.') % self
.min_value
)
179 class DecimalField(Field
):
180 def __init__(self
, max_value
=None, min_value
=None, max_digits
=None, decimal_places
=None, *args
, **kwargs
):
181 self
.max_value
, self
.min_value
= max_value
, min_value
182 self
.max_digits
, self
.decimal_places
= max_digits
, decimal_places
183 Field
.__init
__(self
, *args
, **kwargs
)
185 def clean(self
, value
):
187 Validates that the input is a decimal number. Returns a Decimal
188 instance. Returns None for empty values. Ensures that there are no more
189 than max_digits in the number, and no more than decimal_places digits
190 after the decimal point.
192 super(DecimalField
, self
).clean(value
)
193 if not self
.required
and value
in EMPTY_VALUES
:
195 value
= str(value
).strip()
197 value
= Decimal(value
)
198 except DecimalException
:
199 raise ValidationError(ugettext('Enter a number.'))
200 pieces
= str(value
).lstrip("-").split('.')
201 decimals
= (len(pieces
) == 2) and len(pieces
[1]) or 0
202 digits
= len(pieces
[0])
203 if self
.max_value
is not None and value
> self
.max_value
:
204 raise ValidationError(ugettext('Ensure this value is less than or equal to %s.') % self
.max_value
)
205 if self
.min_value
is not None and value
< self
.min_value
:
206 raise ValidationError(ugettext('Ensure this value is greater than or equal to %s.') % self
.min_value
)
207 if self
.max_digits
is not None and (digits
+ decimals
) > self
.max_digits
:
208 raise ValidationError(ugettext('Ensure that there are no more than %s digits in total.') % self
.max_digits
)
209 if self
.decimal_places
is not None and decimals
> self
.decimal_places
:
210 raise ValidationError(ugettext('Ensure that there are no more than %s decimal places.') % self
.decimal_places
)
211 if self
.max_digits
is not None and self
.decimal_places
is not None and digits
> (self
.max_digits
- self
.decimal_places
):
212 raise ValidationError(ugettext('Ensure that there are no more than %s digits before the decimal point.') % (self
.max_digits
- self
.decimal_places
))
215 DEFAULT_DATE_INPUT_FORMATS
= (
216 '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
217 '%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006'
218 '%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006'
219 '%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006'
220 '%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006'
223 class DateField(Field
):
224 def __init__(self
, input_formats
=None, *args
, **kwargs
):
225 super(DateField
, self
).__init
__(*args
, **kwargs
)
226 self
.input_formats
= input_formats
or DEFAULT_DATE_INPUT_FORMATS
228 def clean(self
, value
):
230 Validates that the input can be converted to a date. Returns a Python
231 datetime.date object.
233 super(DateField
, self
).clean(value
)
234 if value
in EMPTY_VALUES
:
236 if isinstance(value
, datetime
.datetime
):
238 if isinstance(value
, datetime
.date
):
240 for format
in self
.input_formats
:
242 return datetime
.date(*time
.strptime(value
, format
)[:3])
245 raise ValidationError(ugettext(u
'Enter a valid date.'))
247 DEFAULT_TIME_INPUT_FORMATS
= (
248 '%H:%M:%S', # '14:30:59'
252 class TimeField(Field
):
253 def __init__(self
, input_formats
=None, *args
, **kwargs
):
254 super(TimeField
, self
).__init
__(*args
, **kwargs
)
255 self
.input_formats
= input_formats
or DEFAULT_TIME_INPUT_FORMATS
257 def clean(self
, value
):
259 Validates that the input can be converted to a time. Returns a Python
260 datetime.time object.
262 super(TimeField
, self
).clean(value
)
263 if value
in EMPTY_VALUES
:
265 if isinstance(value
, datetime
.time
):
267 for format
in self
.input_formats
:
269 return datetime
.time(*time
.strptime(value
, format
)[3:6])
272 raise ValidationError(ugettext(u
'Enter a valid time.'))
274 DEFAULT_DATETIME_INPUT_FORMATS
= (
275 '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
276 '%Y-%m-%d %H:%M', # '2006-10-25 14:30'
277 '%Y-%m-%d', # '2006-10-25'
278 '%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59'
279 '%m/%d/%Y %H:%M', # '10/25/2006 14:30'
280 '%m/%d/%Y', # '10/25/2006'
281 '%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59'
282 '%m/%d/%y %H:%M', # '10/25/06 14:30'
283 '%m/%d/%y', # '10/25/06'
286 class DateTimeField(Field
):
287 def __init__(self
, input_formats
=None, *args
, **kwargs
):
288 super(DateTimeField
, self
).__init
__(*args
, **kwargs
)
289 self
.input_formats
= input_formats
or DEFAULT_DATETIME_INPUT_FORMATS
291 def clean(self
, value
):
293 Validates that the input can be converted to a datetime. Returns a
294 Python datetime.datetime object.
296 super(DateTimeField
, self
).clean(value
)
297 if value
in EMPTY_VALUES
:
299 if isinstance(value
, datetime
.datetime
):
301 if isinstance(value
, datetime
.date
):
302 return datetime
.datetime(value
.year
, value
.month
, value
.day
)
303 for format
in self
.input_formats
:
305 return datetime
.datetime(*time
.strptime(value
, format
)[:6])
308 raise ValidationError(ugettext(u
'Enter a valid date/time.'))
310 class RegexField(CharField
):
311 def __init__(self
, regex
, max_length
=None, min_length
=None, error_message
=None, *args
, **kwargs
):
313 regex can be either a string or a compiled regular expression object.
314 error_message is an optional error message to use, if
315 'Enter a valid value' is too generic for you.
317 super(RegexField
, self
).__init
__(max_length
, min_length
, *args
, **kwargs
)
318 if isinstance(regex
, basestring
):
319 regex
= re
.compile(regex
)
321 self
.error_message
= error_message
or ugettext(u
'Enter a valid value.')
323 def clean(self
, value
):
325 Validates that the input matches the regular expression. Returns a
328 value
= super(RegexField
, self
).clean(value
)
331 if not self
.regex
.search(value
):
332 raise ValidationError(self
.error_message
)
335 email_re
= re
.compile(
336 r
"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
337 r
'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string
338 r
')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re
.IGNORECASE
) # domain
340 class EmailField(RegexField
):
341 def __init__(self
, max_length
=None, min_length
=None, *args
, **kwargs
):
342 RegexField
.__init
__(self
, email_re
, max_length
, min_length
,
343 ugettext(u
'Enter a valid e-mail address.'), *args
, **kwargs
)
346 from django
.conf
import settings
347 URL_VALIDATOR_USER_AGENT
= settings
.URL_VALIDATOR_USER_AGENT
348 except (ImportError, EnvironmentError):
349 # It's OK if Django settings aren't configured.
350 URL_VALIDATOR_USER_AGENT
= 'Django (http://www.djangoproject.com/)'
352 class UploadedFile(StrAndUnicode
):
353 "A wrapper for files uploaded in a FileField"
354 def __init__(self
, filename
, content
):
355 self
.filename
= filename
356 self
.content
= content
358 def __unicode__(self
):
360 The unicode representation is the filename, so that the pre-database-insertion
361 logic can use UploadedFile objects
365 class FileField(Field
):
367 def __init__(self
, *args
, **kwargs
):
368 super(FileField
, self
).__init
__(*args
, **kwargs
)
370 def clean(self
, data
):
371 super(FileField
, self
).clean(data
)
372 if not self
.required
and data
in EMPTY_VALUES
:
375 f
= UploadedFile(data
['filename'], data
['content'])
377 raise ValidationError(ugettext(u
"No file was submitted. Check the encoding type on the form."))
379 raise ValidationError(ugettext(u
"No file was submitted."))
381 raise ValidationError(ugettext(u
"The submitted file is empty."))
384 class ImageField(FileField
):
385 def clean(self
, data
):
387 Checks that the file-upload field data contains a valid image (GIF, JPG,
388 PNG, possibly others -- whatever the Python Imaging Library supports).
390 f
= super(ImageField
, self
).clean(data
)
393 from PIL
import Image
394 from cStringIO
import StringIO
396 # load() is the only method that can spot a truncated JPEG,
397 # but it cannot be called sanely after verify()
398 trial_image
= Image
.open(StringIO(f
.content
))
400 # verify() is the only method that can spot a corrupt PNG,
401 # but it must be called immediately after the constructor
402 trial_image
= Image
.open(StringIO(f
.content
))
404 except Exception: # Python Imaging Library doesn't recognize it as an image
405 raise ValidationError(ugettext(u
"Upload a valid image. The file you uploaded was either not an image or a corrupted image."))
409 r
'^https?://' # http:// or https://
410 r
'(?:(?:[A-Z0-9-]+\.)+[A-Z]{2,6}|' #domain...
411 r
'localhost|' #localhost...
412 r
'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
413 r
'(?::\d+)?' # optional port
414 r
'(?:/?|/\S+)$', re
.IGNORECASE
)
416 class URLField(RegexField
):
417 def __init__(self
, max_length
=None, min_length
=None, verify_exists
=False,
418 validator_user_agent
=URL_VALIDATOR_USER_AGENT
, *args
, **kwargs
):
419 super(URLField
, self
).__init
__(url_re
, max_length
, min_length
, ugettext(u
'Enter a valid URL.'), *args
, **kwargs
)
420 self
.verify_exists
= verify_exists
421 self
.user_agent
= validator_user_agent
423 def clean(self
, value
):
424 # If no URL scheme given, assume http://
425 if value
and '://' not in value
:
426 value
= u
'http://%s' % value
427 value
= super(URLField
, self
).clean(value
)
430 if self
.verify_exists
:
432 from django
.conf
import settings
434 "Accept": "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
435 "Accept-Language": "en-us,en;q=0.5",
436 "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
437 "Connection": "close",
438 "User-Agent": self
.user_agent
,
441 req
= urllib2
.Request(value
, None, headers
)
442 u
= urllib2
.urlopen(req
)
444 raise ValidationError(ugettext(u
'Enter a valid URL.'))
445 except: # urllib2.URLError, httplib.InvalidURL, etc.
446 raise ValidationError(ugettext(u
'This URL appears to be a broken link.'))
449 class BooleanField(Field
):
450 widget
= CheckboxInput
452 def clean(self
, value
):
453 "Returns a Python boolean object."
454 super(BooleanField
, self
).clean(value
)
455 # Explicitly check for the string '0', which is what as hidden field
456 # will submit for False.
461 class NullBooleanField(BooleanField
):
463 A field whose valid values are None, True and False. Invalid values are
466 widget
= NullBooleanSelect
468 def clean(self
, value
):
469 return {True: True, False: False}.get(value
, None)
471 class ChoiceField(Field
):
474 def __init__(self
, choices
=(), required
=True, widget
=None, label
=None, initial
=None, help_text
=None):
475 super(ChoiceField
, self
).__init
__(required
, widget
, label
, initial
, help_text
)
476 self
.choices
= choices
478 def _get_choices(self
):
481 def _set_choices(self
, value
):
482 # Setting choices also sets the choices on the widget.
483 # choices can be any iterable, but we call list() on it because
484 # it will be consumed more than once.
485 self
._choices
= self
.widget
.choices
= list(value
)
487 choices
= property(_get_choices
, _set_choices
)
489 def clean(self
, value
):
491 Validates that the input is in self.choices.
493 value
= super(ChoiceField
, self
).clean(value
)
494 if value
in EMPTY_VALUES
:
496 value
= smart_unicode(value
)
499 valid_values
= set([smart_unicode(k
) for k
, v
in self
.choices
])
500 if value
not in valid_values
:
501 raise ValidationError(ugettext(u
'Select a valid choice. That choice is not one of the available choices.'))
504 class MultipleChoiceField(ChoiceField
):
505 hidden_widget
= MultipleHiddenInput
506 widget
= SelectMultiple
508 def clean(self
, value
):
510 Validates that the input is a list or tuple.
512 if self
.required
and not value
:
513 raise ValidationError(ugettext(u
'This field is required.'))
514 elif not self
.required
and not value
:
516 if not isinstance(value
, (list, tuple)):
517 raise ValidationError(ugettext(u
'Enter a list of values.'))
518 new_value
= [smart_unicode(val
) for val
in value
]
519 # Validate that each value in the value list is in self.choices.
520 valid_values
= set([smart_unicode(k
) for k
, v
in self
.choices
])
521 for val
in new_value
:
522 if val
not in valid_values
:
523 raise ValidationError(ugettext(u
'Select a valid choice. %s is not one of the available choices.') % val
)
526 class ComboField(Field
):
528 A Field whose clean() method calls multiple Field clean() methods.
530 def __init__(self
, fields
=(), *args
, **kwargs
):
531 super(ComboField
, self
).__init
__(*args
, **kwargs
)
532 # Set 'required' to False on the individual fields, because the
533 # required validation will be handled by ComboField, not by those
539 def clean(self
, value
):
541 Validates the given value against all of self.fields, which is a
542 list of Field instances.
544 super(ComboField
, self
).clean(value
)
545 for field
in self
.fields
:
546 value
= field
.clean(value
)
549 class MultiValueField(Field
):
551 A Field that aggregates the logic of multiple Fields.
553 Its clean() method takes a "decompressed" list of values, which are then
554 cleaned into a single value according to self.fields. Each value in
555 this list is cleaned by the corresponding field -- the first value is
556 cleaned by the first field, the second value is cleaned by the second
557 field, etc. Once all fields are cleaned, the list of clean values is
558 "compressed" into a single value.
560 Subclasses should not have to implement clean(). Instead, they must
561 implement compress(), which takes a list of valid values and returns a
562 "compressed" version of those values -- a single value.
564 You'll probably want to use this with MultiWidget.
566 def __init__(self
, fields
=(), *args
, **kwargs
):
567 super(MultiValueField
, self
).__init
__(*args
, **kwargs
)
568 # Set 'required' to False on the individual fields, because the
569 # required validation will be handled by MultiValueField, not by those
575 def clean(self
, value
):
577 Validates every value in the given list. A value is validated against
578 the corresponding Field in self.fields.
580 For example, if this MultiValueField was instantiated with
581 fields=(DateField(), TimeField()), clean() would call
582 DateField.clean(value[0]) and TimeField.clean(value[1]).
586 if not value
or isinstance(value
, (list, tuple)):
587 if not value
or not [v
for v
in value
if v
not in EMPTY_VALUES
]:
589 raise ValidationError(ugettext(u
'This field is required.'))
591 return self
.compress([])
593 raise ValidationError(ugettext(u
'Enter a list of values.'))
594 for i
, field
in enumerate(self
.fields
):
596 field_value
= value
[i
]
599 if self
.required
and field_value
in EMPTY_VALUES
:
600 raise ValidationError(ugettext(u
'This field is required.'))
602 clean_data
.append(field
.clean(field_value
))
603 except ValidationError
, e
:
604 # Collect all validation errors in a single list, which we'll
605 # raise at the end of clean(), rather than raising a single
606 # exception for the first error we encounter.
607 errors
.extend(e
.messages
)
609 raise ValidationError(errors
)
610 return self
.compress(clean_data
)
612 def compress(self
, data_list
):
614 Returns a single value for the given list of values. The values can be
617 For example, if this MultiValueField was instantiated with
618 fields=(DateField(), TimeField()), this might return a datetime
619 object created by combining the date and time in data_list.
621 raise NotImplementedError('Subclasses must implement this method.')
623 class SplitDateTimeField(MultiValueField
):
624 def __init__(self
, *args
, **kwargs
):
625 fields
= (DateField(), TimeField())
626 super(SplitDateTimeField
, self
).__init
__(fields
, *args
, **kwargs
)
628 def compress(self
, data_list
):
630 # Raise a validation error if time or date is empty
631 # (possible if SplitDateTimeField has required=False).
632 if data_list
[0] in EMPTY_VALUES
:
633 raise ValidationError(ugettext(u
'Enter a valid date.'))
634 if data_list
[1] in EMPTY_VALUES
:
635 raise ValidationError(ugettext(u
'Enter a valid time.'))
636 return datetime
.datetime
.combine(*data_list
)
639 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}$')
641 class IPAddressField(RegexField
):
642 def __init__(self
, *args
, **kwargs
):
643 RegexField
.__init
__(self
, ipv4_re
,
644 error_message
=ugettext(u
'Enter a valid IPv4 address.'),