Fixed #4067 -- Fixed validation of IPAddressFields in newforms. Thanks to neils and...
[django.git] / django / newforms / fields.py
bloba39987e1b582db3b7c0d8c003dd61377b163f8b7
1 """
2 Field classes
3 """
5 import copy
6 import datetime
7 import re
8 import time
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
16 try:
17 from decimal import Decimal, DecimalException
18 except ImportError:
19 from django.utils._decimal import Decimal, DecimalException
21 __all__ = (
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, '')
35 try:
36 set
37 except NameError:
38 from sets import Set as set # Python 2.3 fallback
40 try:
41 from decimal import Decimal
42 except ImportError:
43 from django.utils._decimal import Decimal # Python 2.3 fallback
45 class Field(object):
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.
50 creation_counter = 0
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.
54 # True by default.
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
62 # Form.
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.
66 if label is not None:
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):
72 widget = widget()
74 # Hook into self.widget_attrs() for any Field-specific HTML attributes.
75 extra_attrs = self.widget_attrs(widget)
76 if extra_attrs:
77 widget.attrs.update(extra_attrs)
79 self.widget = widget
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):
86 """
87 Validates the given value and returns its "cleaned" value as an
88 appropriate Python object.
90 Raises ValidationError for any errors.
91 """
92 if self.required and value in EMPTY_VALUES:
93 raise ValidationError(ugettext(u'This field is required.'))
94 return value
96 def widget_attrs(self, widget):
97 """
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
100 Field.
102 return {}
104 def __deepcopy__(self, memo):
105 result = copy.copy(self)
106 memo[id(self)] = result
107 result.widget = copy.deepcopy(self.widget, memo)
108 return result
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:
119 return u''
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})
126 return value
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:
145 return None
146 try:
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)
154 return 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:
168 return None
169 try:
170 value = float(value)
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)
177 return 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:
194 return None
195 value = str(value).strip()
196 try:
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))
213 return value
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:
235 return None
236 if isinstance(value, datetime.datetime):
237 return value.date()
238 if isinstance(value, datetime.date):
239 return value
240 for format in self.input_formats:
241 try:
242 return datetime.date(*time.strptime(value, format)[:3])
243 except ValueError:
244 continue
245 raise ValidationError(ugettext(u'Enter a valid date.'))
247 DEFAULT_TIME_INPUT_FORMATS = (
248 '%H:%M:%S', # '14:30:59'
249 '%H:%M', # '14:30'
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:
264 return None
265 if isinstance(value, datetime.time):
266 return value
267 for format in self.input_formats:
268 try:
269 return datetime.time(*time.strptime(value, format)[3:6])
270 except ValueError:
271 continue
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:
298 return None
299 if isinstance(value, datetime.datetime):
300 return value
301 if isinstance(value, datetime.date):
302 return datetime.datetime(value.year, value.month, value.day)
303 for format in self.input_formats:
304 try:
305 return datetime.datetime(*time.strptime(value, format)[:6])
306 except ValueError:
307 continue
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)
320 self.regex = 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
326 Unicode object.
328 value = super(RegexField, self).clean(value)
329 if value == u'':
330 return value
331 if not self.regex.search(value):
332 raise ValidationError(self.error_message)
333 return value
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)
345 try:
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
363 return self.filename
365 class FileField(Field):
366 widget = FileInput
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:
373 return None
374 try:
375 f = UploadedFile(data['filename'], data['content'])
376 except TypeError:
377 raise ValidationError(ugettext(u"No file was submitted. Check the encoding type on the form."))
378 except KeyError:
379 raise ValidationError(ugettext(u"No file was submitted."))
380 if not f.content:
381 raise ValidationError(ugettext(u"The submitted file is empty."))
382 return f
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)
391 if f is None:
392 return None
393 from PIL import Image
394 from cStringIO import StringIO
395 try:
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))
399 trial_image.load()
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))
403 trial_image.verify()
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."))
406 return f
408 url_re = re.compile(
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)
428 if value == u'':
429 return value
430 if self.verify_exists:
431 import urllib2
432 from django.conf import settings
433 headers = {
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,
440 try:
441 req = urllib2.Request(value, None, headers)
442 u = urllib2.urlopen(req)
443 except ValueError:
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.'))
447 return value
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.
457 if value == '0':
458 return False
459 return bool(value)
461 class NullBooleanField(BooleanField):
463 A field whose valid values are None, True and False. Invalid values are
464 cleaned to None.
466 widget = NullBooleanSelect
468 def clean(self, value):
469 return {True: True, False: False}.get(value, None)
471 class ChoiceField(Field):
472 widget = Select
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):
479 return self._choices
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:
495 value = u''
496 value = smart_unicode(value)
497 if value == u'':
498 return 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.'))
502 return value
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:
515 return []
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)
524 return new_value
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
534 # individual fields.
535 for f in fields:
536 f.required = False
537 self.fields = fields
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)
547 return 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
570 # individual fields.
571 for f in fields:
572 f.required = False
573 self.fields = fields
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]).
584 clean_data = []
585 errors = ErrorList()
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]:
588 if self.required:
589 raise ValidationError(ugettext(u'This field is required.'))
590 else:
591 return self.compress([])
592 else:
593 raise ValidationError(ugettext(u'Enter a list of values.'))
594 for i, field in enumerate(self.fields):
595 try:
596 field_value = value[i]
597 except IndexError:
598 field_value = None
599 if self.required and field_value in EMPTY_VALUES:
600 raise ValidationError(ugettext(u'This field is required.'))
601 try:
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)
608 if errors:
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
615 assumed to be valid.
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):
629 if 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)
637 return None
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.'),
645 *args, **kwargs)