| 1 |
""" |
|---|
| 2 |
Field classes. |
|---|
| 3 |
""" |
|---|
| 4 |
|
|---|
| 5 |
import copy |
|---|
| 6 |
import datetime |
|---|
| 7 |
import os |
|---|
| 8 |
import re |
|---|
| 9 |
import time |
|---|
| 10 |
try: |
|---|
| 11 |
from cStringIO import StringIO |
|---|
| 12 |
except ImportError: |
|---|
| 13 |
from StringIO import StringIO |
|---|
| 14 |
|
|---|
| 15 |
# Python 2.3 fallbacks |
|---|
| 16 |
try: |
|---|
| 17 |
from decimal import Decimal, DecimalException |
|---|
| 18 |
except ImportError: |
|---|
| 19 |
from django.utils._decimal import Decimal, DecimalException |
|---|
| 20 |
try: |
|---|
| 21 |
set |
|---|
| 22 |
except NameError: |
|---|
| 23 |
from sets import Set as set |
|---|
| 24 |
|
|---|
| 25 |
from django.utils.translation import ugettext_lazy as _ |
|---|
| 26 |
from django.utils.encoding import StrAndUnicode, smart_unicode, smart_str |
|---|
| 27 |
|
|---|
| 28 |
from util import ErrorList, ValidationError |
|---|
| 29 |
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput |
|---|
| 30 |
from django.core.files.uploadedfile import SimpleUploadedFile as UploadedFile |
|---|
| 31 |
|
|---|
| 32 |
__all__ = ( |
|---|
| 33 |
'Field', 'CharField', 'IntegerField', |
|---|
| 34 |
'DEFAULT_DATE_INPUT_FORMATS', 'DateField', |
|---|
| 35 |
'DEFAULT_TIME_INPUT_FORMATS', 'TimeField', |
|---|
| 36 |
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField', |
|---|
| 37 |
'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', |
|---|
| 38 |
'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', |
|---|
| 39 |
'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', |
|---|
| 40 |
'SplitDateTimeField', 'IPAddressField', 'FilePathField', |
|---|
| 41 |
) |
|---|
| 42 |
|
|---|
| 43 |
# These values, if given to to_python(), will trigger the self.required check. |
|---|
| 44 |
EMPTY_VALUES = (None, '') |
|---|
| 45 |
|
|---|
| 46 |
|
|---|
| 47 |
class Field(object): |
|---|
| 48 |
widget = TextInput # Default widget to use when rendering this type of Field. |
|---|
| 49 |
hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden". |
|---|
| 50 |
default_error_messages = { |
|---|
| 51 |
'required': _(u'This field is required.'), |
|---|
| 52 |
'invalid': _(u'Enter a valid value.'), |
|---|
| 53 |
} |
|---|
| 54 |
|
|---|
| 55 |
# Tracks each time a Field instance is created. Used to retain order. |
|---|
| 56 |
creation_counter = 0 |
|---|
| 57 |
|
|---|
| 58 |
def __init__(self, required=True, widget=None, label=None, initial=None, |
|---|
| 59 |
help_text=None, error_messages=None): |
|---|
| 60 |
# required -- Boolean that specifies whether the field is required. |
|---|
| 61 |
# True by default. |
|---|
| 62 |
# widget -- A Widget class, or instance of a Widget class, that should |
|---|
| 63 |
# be used for this Field when displaying it. Each Field has a |
|---|
| 64 |
# default Widget that it'll use if you don't specify this. In |
|---|
| 65 |
# most cases, the default widget is TextInput. |
|---|
| 66 |
# label -- A verbose name for this field, for use in displaying this |
|---|
| 67 |
# field in a form. By default, Django will use a "pretty" |
|---|
| 68 |
# version of the form field name, if the Field is part of a |
|---|
| 69 |
# Form. |
|---|
| 70 |
# initial -- A value to use in this Field's initial display. This value |
|---|
| 71 |
# is *not* used as a fallback if data isn't given. |
|---|
| 72 |
# help_text -- An optional string to use as "help text" for this Field. |
|---|
| 73 |
if label is not None: |
|---|
| 74 |
label = smart_unicode(label) |
|---|
| 75 |
self.required, self.label, self.initial = required, label, initial |
|---|
| 76 |
self.help_text = smart_unicode(help_text or '') |
|---|
| 77 |
widget = widget or self.widget |
|---|
| 78 |
if isinstance(widget, type): |
|---|
| 79 |
widget = widget() |
|---|
| 80 |
|
|---|
| 81 |
# Hook into self.widget_attrs() for any Field-specific HTML attributes. |
|---|
| 82 |
extra_attrs = self.widget_attrs(widget) |
|---|
| 83 |
if extra_attrs: |
|---|
| 84 |
widget.attrs.update(extra_attrs) |
|---|
| 85 |
|
|---|
| 86 |
self.widget = widget |
|---|
| 87 |
|
|---|
| 88 |
# Increase the creation counter, and save our local copy. |
|---|
| 89 |
self.creation_counter = Field.creation_counter |
|---|
| 90 |
Field.creation_counter += 1 |
|---|
| 91 |
|
|---|
| 92 |
def set_class_error_messages(messages, klass): |
|---|
| 93 |
for base_class in klass.__bases__: |
|---|
| 94 |
set_class_error_messages(messages, base_class) |
|---|
| 95 |
messages.update(getattr(klass, 'default_error_messages', {})) |
|---|
| 96 |
|
|---|
| 97 |
messages = {} |
|---|
| 98 |
set_class_error_messages(messages, self.__class__) |
|---|
| 99 |
messages.update(error_messages or {}) |
|---|
| 100 |
self.error_messages = messages |
|---|
| 101 |
|
|---|
| 102 |
def clean(self, value): |
|---|
| 103 |
""" |
|---|
| 104 |
Validates the given value and returns its "cleaned" value as an |
|---|
| 105 |
appropriate Python object. |
|---|
| 106 |
|
|---|
| 107 |
Raises ValidationError for any errors. |
|---|
| 108 |
""" |
|---|
| 109 |
if self.required and value in EMPTY_VALUES: |
|---|
| 110 |
raise ValidationError(self.error_messages['required']) |
|---|
| 111 |
return value |
|---|
| 112 |
|
|---|
| 113 |
def widget_attrs(self, widget): |
|---|
| 114 |
""" |
|---|
| 115 |
Given a Widget instance (*not* a Widget class), returns a dictionary of |
|---|
| 116 |
any HTML attributes that should be added to the Widget, based on this |
|---|
| 117 |
Field. |
|---|
| 118 |
""" |
|---|
| 119 |
return {} |
|---|
| 120 |
|
|---|
| 121 |
def __deepcopy__(self, memo): |
|---|
| 122 |
result = copy.copy(self) |
|---|
| 123 |
memo[id(self)] = result |
|---|
| 124 |
result.widget = copy.deepcopy(self.widget, memo) |
|---|
| 125 |
return result |
|---|
| 126 |
|
|---|
| 127 |
class CharField(Field): |
|---|
| 128 |
default_error_messages = { |
|---|
| 129 |
'max_length': _(u'Ensure this value has at most %(max)d characters (it has %(length)d).'), |
|---|
| 130 |
'min_length': _(u'Ensure this value has at least %(min)d characters (it has %(length)d).'), |
|---|
| 131 |
} |
|---|
| 132 |
|
|---|
| 133 |
def __init__(self, max_length=None, min_length=None, *args, **kwargs): |
|---|
| 134 |
self.max_length, self.min_length = max_length, min_length |
|---|
| 135 |
super(CharField, self).__init__(*args, **kwargs) |
|---|
| 136 |
|
|---|
| 137 |
def clean(self, value): |
|---|
| 138 |
"Validates max_length and min_length. Returns a Unicode object." |
|---|
| 139 |
super(CharField, self).clean(value) |
|---|
| 140 |
if value in EMPTY_VALUES: |
|---|
| 141 |
return u'' |
|---|
| 142 |
value = smart_unicode(value) |
|---|
| 143 |
value_length = len(value) |
|---|
| 144 |
if self.max_length is not None and value_length > self.max_length: |
|---|
| 145 |
raise ValidationError(self.error_messages['max_length'] % {'max': self.max_length, 'length': value_length}) |
|---|
| 146 |
if self.min_length is not None and value_length < self.min_length: |
|---|
| 147 |
raise ValidationError(self.error_messages['min_length'] % {'min': self.min_length, 'length': value_length}) |
|---|
| 148 |
return value |
|---|
| 149 |
|
|---|
| 150 |
def widget_attrs(self, widget): |
|---|
| 151 |
if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)): |
|---|
| 152 |
# The HTML attribute is maxlength, not max_length. |
|---|
| 153 |
return {'maxlength': str(self.max_length)} |
|---|
| 154 |
|
|---|
| 155 |
class IntegerField(Field): |
|---|
| 156 |
default_error_messages = { |
|---|
| 157 |
'invalid': _(u'Enter a whole number.'), |
|---|
| 158 |
'max_value': _(u'Ensure this value is less than or equal to %s.'), |
|---|
| 159 |
'min_value': _(u'Ensure this value is greater than or equal to %s.'), |
|---|
| 160 |
} |
|---|
| 161 |
|
|---|
| 162 |
def __init__(self, max_value=None, min_value=None, *args, **kwargs): |
|---|
| 163 |
self.max_value, self.min_value = max_value, min_value |
|---|
| 164 |
super(IntegerField, self).__init__(*args, **kwargs) |
|---|
| 165 |
|
|---|
| 166 |
def clean(self, value): |
|---|
| 167 |
""" |
|---|
| 168 |
Validates that int() can be called on the input. Returns the result |
|---|
| 169 |
of int(). Returns None for empty values. |
|---|
| 170 |
""" |
|---|
| 171 |
super(IntegerField, self).clean(value) |
|---|
| 172 |
if value in EMPTY_VALUES: |
|---|
| 173 |
return None |
|---|
| 174 |
try: |
|---|
| 175 |
value = int(str(value)) |
|---|
| 176 |
except (ValueError, TypeError): |
|---|
| 177 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 178 |
if self.max_value is not None and value > self.max_value: |
|---|
| 179 |
raise ValidationError(self.error_messages['max_value'] % self.max_value) |
|---|
| 180 |
if self.min_value is not None and value < self.min_value: |
|---|
| 181 |
raise ValidationError(self.error_messages['min_value'] % self.min_value) |
|---|
| 182 |
return value |
|---|
| 183 |
|
|---|
| 184 |
class FloatField(Field): |
|---|
| 185 |
default_error_messages = { |
|---|
| 186 |
'invalid': _(u'Enter a number.'), |
|---|
| 187 |
'max_value': _(u'Ensure this value is less than or equal to %s.'), |
|---|
| 188 |
'min_value': _(u'Ensure this value is greater than or equal to %s.'), |
|---|
| 189 |
} |
|---|
| 190 |
|
|---|
| 191 |
def __init__(self, max_value=None, min_value=None, *args, **kwargs): |
|---|
| 192 |
self.max_value, self.min_value = max_value, min_value |
|---|
| 193 |
Field.__init__(self, *args, **kwargs) |
|---|
| 194 |
|
|---|
| 195 |
def clean(self, value): |
|---|
| 196 |
""" |
|---|
| 197 |
Validates that float() can be called on the input. Returns a float. |
|---|
| 198 |
Returns None for empty values. |
|---|
| 199 |
""" |
|---|
| 200 |
super(FloatField, self).clean(value) |
|---|
| 201 |
if not self.required and value in EMPTY_VALUES: |
|---|
| 202 |
return None |
|---|
| 203 |
try: |
|---|
| 204 |
value = float(value) |
|---|
| 205 |
except (ValueError, TypeError): |
|---|
| 206 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 207 |
if self.max_value is not None and value > self.max_value: |
|---|
| 208 |
raise ValidationError(self.error_messages['max_value'] % self.max_value) |
|---|
| 209 |
if self.min_value is not None and value < self.min_value: |
|---|
| 210 |
raise ValidationError(self.error_messages['min_value'] % self.min_value) |
|---|
| 211 |
return value |
|---|
| 212 |
|
|---|
| 213 |
class DecimalField(Field): |
|---|
| 214 |
default_error_messages = { |
|---|
| 215 |
'invalid': _(u'Enter a number.'), |
|---|
| 216 |
'max_value': _(u'Ensure this value is less than or equal to %s.'), |
|---|
| 217 |
'min_value': _(u'Ensure this value is greater than or equal to %s.'), |
|---|
| 218 |
'max_digits': _('Ensure that there are no more than %s digits in total.'), |
|---|
| 219 |
'max_decimal_places': _('Ensure that there are no more than %s decimal places.'), |
|---|
| 220 |
'max_whole_digits': _('Ensure that there are no more than %s digits before the decimal point.') |
|---|
| 221 |
} |
|---|
| 222 |
|
|---|
| 223 |
def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs): |
|---|
| 224 |
self.max_value, self.min_value = max_value, min_value |
|---|
| 225 |
self.max_digits, self.decimal_places = max_digits, decimal_places |
|---|
| 226 |
Field.__init__(self, *args, **kwargs) |
|---|
| 227 |
|
|---|
| 228 |
def clean(self, value): |
|---|
| 229 |
""" |
|---|
| 230 |
Validates that the input is a decimal number. Returns a Decimal |
|---|
| 231 |
instance. Returns None for empty values. Ensures that there are no more |
|---|
| 232 |
than max_digits in the number, and no more than decimal_places digits |
|---|
| 233 |
after the decimal point. |
|---|
| 234 |
""" |
|---|
| 235 |
super(DecimalField, self).clean(value) |
|---|
| 236 |
if not self.required and value in EMPTY_VALUES: |
|---|
| 237 |
return None |
|---|
| 238 |
value = smart_str(value).strip() |
|---|
| 239 |
try: |
|---|
| 240 |
value = Decimal(value) |
|---|
| 241 |
except DecimalException: |
|---|
| 242 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 243 |
pieces = str(value).lstrip("-").split('.') |
|---|
| 244 |
decimals = (len(pieces) == 2) and len(pieces[1]) or 0 |
|---|
| 245 |
digits = len(pieces[0]) |
|---|
| 246 |
if self.max_value is not None and value > self.max_value: |
|---|
| 247 |
raise ValidationError(self.error_messages['max_value'] % self.max_value) |
|---|
| 248 |
if self.min_value is not None and value < self.min_value: |
|---|
| 249 |
raise ValidationError(self.error_messages['min_value'] % self.min_value) |
|---|
| 250 |
if self.max_digits is not None and (digits + decimals) > self.max_digits: |
|---|
| 251 |
raise ValidationError(self.error_messages['max_digits'] % self.max_digits) |
|---|
| 252 |
if self.decimal_places is not None and decimals > self.decimal_places: |
|---|
| 253 |
raise ValidationError(self.error_messages['max_decimal_places'] % self.decimal_places) |
|---|
| 254 |
if self.max_digits is not None and self.decimal_places is not None and digits > (self.max_digits - self.decimal_places): |
|---|
| 255 |
raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places)) |
|---|
| 256 |
return value |
|---|
| 257 |
|
|---|
| 258 |
DEFAULT_DATE_INPUT_FORMATS = ( |
|---|
| 259 |
'%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06' |
|---|
| 260 |
'%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006' |
|---|
| 261 |
'%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006' |
|---|
| 262 |
'%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006' |
|---|
| 263 |
'%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006' |
|---|
| 264 |
) |
|---|
| 265 |
|
|---|
| 266 |
class DateField(Field): |
|---|
| 267 |
default_error_messages = { |
|---|
| 268 |
'invalid': _(u'Enter a valid date.'), |
|---|
| 269 |
} |
|---|
| 270 |
|
|---|
| 271 |
def __init__(self, input_formats=None, *args, **kwargs): |
|---|
| 272 |
super(DateField, self).__init__(*args, **kwargs) |
|---|
| 273 |
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS |
|---|
| 274 |
|
|---|
| 275 |
def clean(self, value): |
|---|
| 276 |
""" |
|---|
| 277 |
Validates that the input can be converted to a date. Returns a Python |
|---|
| 278 |
datetime.date object. |
|---|
| 279 |
""" |
|---|
| 280 |
super(DateField, self).clean(value) |
|---|
| 281 |
if value in EMPTY_VALUES: |
|---|
| 282 |
return None |
|---|
| 283 |
if isinstance(value, datetime.datetime): |
|---|
| 284 |
return value.date() |
|---|
| 285 |
if isinstance(value, datetime.date): |
|---|
| 286 |
return value |
|---|
| 287 |
for format in self.input_formats: |
|---|
| 288 |
try: |
|---|
| 289 |
return datetime.date(*time.strptime(value, format)[:3]) |
|---|
| 290 |
except ValueError: |
|---|
| 291 |
continue |
|---|
| 292 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 293 |
|
|---|
| 294 |
DEFAULT_TIME_INPUT_FORMATS = ( |
|---|
| 295 |
'%H:%M:%S', # '14:30:59' |
|---|
| 296 |
'%H:%M', # '14:30' |
|---|
| 297 |
) |
|---|
| 298 |
|
|---|
| 299 |
class TimeField(Field): |
|---|
| 300 |
default_error_messages = { |
|---|
| 301 |
'invalid': _(u'Enter a valid time.') |
|---|
| 302 |
} |
|---|
| 303 |
|
|---|
| 304 |
def __init__(self, input_formats=None, *args, **kwargs): |
|---|
| 305 |
super(TimeField, self).__init__(*args, **kwargs) |
|---|
| 306 |
self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS |
|---|
| 307 |
|
|---|
| 308 |
def clean(self, value): |
|---|
| 309 |
""" |
|---|
| 310 |
Validates that the input can be converted to a time. Returns a Python |
|---|
| 311 |
datetime.time object. |
|---|
| 312 |
""" |
|---|
| 313 |
super(TimeField, self).clean(value) |
|---|
| 314 |
if value in EMPTY_VALUES: |
|---|
| 315 |
return None |
|---|
| 316 |
if isinstance(value, datetime.time): |
|---|
| 317 |
return value |
|---|
| 318 |
for format in self.input_formats: |
|---|
| 319 |
try: |
|---|
| 320 |
return datetime.time(*time.strptime(value, format)[3:6]) |
|---|
| 321 |
except ValueError: |
|---|
| 322 |
continue |
|---|
| 323 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 324 |
|
|---|
| 325 |
DEFAULT_DATETIME_INPUT_FORMATS = ( |
|---|
| 326 |
'%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' |
|---|
| 327 |
'%Y-%m-%d %H:%M', # '2006-10-25 14:30' |
|---|
| 328 |
'%Y-%m-%d', # '2006-10-25' |
|---|
| 329 |
'%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59' |
|---|
| 330 |
'%m/%d/%Y %H:%M', # '10/25/2006 14:30' |
|---|
| 331 |
'%m/%d/%Y', # '10/25/2006' |
|---|
| 332 |
'%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59' |
|---|
| 333 |
'%m/%d/%y %H:%M', # '10/25/06 14:30' |
|---|
| 334 |
'%m/%d/%y', # '10/25/06' |
|---|
| 335 |
) |
|---|
| 336 |
|
|---|
| 337 |
class DateTimeField(Field): |
|---|
| 338 |
widget = DateTimeInput |
|---|
| 339 |
default_error_messages = { |
|---|
| 340 |
'invalid': _(u'Enter a valid date/time.'), |
|---|
| 341 |
} |
|---|
| 342 |
|
|---|
| 343 |
def __init__(self, input_formats=None, *args, **kwargs): |
|---|
| 344 |
super(DateTimeField, self).__init__(*args, **kwargs) |
|---|
| 345 |
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS |
|---|
| 346 |
|
|---|
| 347 |
def clean(self, value): |
|---|
| 348 |
""" |
|---|
| 349 |
Validates that the input can be converted to a datetime. Returns a |
|---|
| 350 |
Python datetime.datetime object. |
|---|
| 351 |
""" |
|---|
| 352 |
super(DateTimeField, self).clean(value) |
|---|
| 353 |
if value in EMPTY_VALUES: |
|---|
| 354 |
return None |
|---|
| 355 |
if isinstance(value, datetime.datetime): |
|---|
| 356 |
return value |
|---|
| 357 |
if isinstance(value, datetime.date): |
|---|
| 358 |
return datetime.datetime(value.year, value.month, value.day) |
|---|
| 359 |
if isinstance(value, list): |
|---|
| 360 |
# Input comes from a SplitDateTimeWidget, for example. So, it's two |
|---|
| 361 |
# components: date and time. |
|---|
| 362 |
if len(value) != 2: |
|---|
| 363 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 364 |
value = '%s %s' % tuple(value) |
|---|
| 365 |
for format in self.input_formats: |
|---|
| 366 |
try: |
|---|
| 367 |
return datetime.datetime(*time.strptime(value, format)[:6]) |
|---|
| 368 |
except ValueError: |
|---|
| 369 |
continue |
|---|
| 370 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 371 |
|
|---|
| 372 |
class RegexField(CharField): |
|---|
| 373 |
def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs): |
|---|
| 374 |
""" |
|---|
| 375 |
regex can be either a string or a compiled regular expression object. |
|---|
| 376 |
error_message is an optional error message to use, if |
|---|
| 377 |
'Enter a valid value' is too generic for you. |
|---|
| 378 |
""" |
|---|
| 379 |
# error_message is just kept for backwards compatibility: |
|---|
| 380 |
if error_message: |
|---|
| 381 |
error_messages = kwargs.get('error_messages') or {} |
|---|
| 382 |
error_messages['invalid'] = error_message |
|---|
| 383 |
kwargs['error_messages'] = error_messages |
|---|
| 384 |
super(RegexField, self).__init__(max_length, min_length, *args, **kwargs) |
|---|
| 385 |
if isinstance(regex, basestring): |
|---|
| 386 |
regex = re.compile(regex) |
|---|
| 387 |
self.regex = regex |
|---|
| 388 |
|
|---|
| 389 |
def clean(self, value): |
|---|
| 390 |
""" |
|---|
| 391 |
Validates that the input matches the regular expression. Returns a |
|---|
| 392 |
Unicode object. |
|---|
| 393 |
""" |
|---|
| 394 |
value = super(RegexField, self).clean(value) |
|---|
| 395 |
if value == u'': |
|---|
| 396 |
return value |
|---|
| 397 |
if not self.regex.search(value): |
|---|
| 398 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 399 |
return value |
|---|
| 400 |
|
|---|
| 401 |
email_re = re.compile( |
|---|
| 402 |
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom |
|---|
| 403 |
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string |
|---|
| 404 |
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain |
|---|
| 405 |
|
|---|
| 406 |
class EmailField(RegexField): |
|---|
| 407 |
default_error_messages = { |
|---|
| 408 |
'invalid': _(u'Enter a valid e-mail address.'), |
|---|
| 409 |
} |
|---|
| 410 |
|
|---|
| 411 |
def __init__(self, max_length=None, min_length=None, *args, **kwargs): |
|---|
| 412 |
RegexField.__init__(self, email_re, max_length, min_length, *args, |
|---|
| 413 |
**kwargs) |
|---|
| 414 |
|
|---|
| 415 |
try: |
|---|
| 416 |
from django.conf import settings |
|---|
| 417 |
URL_VALIDATOR_USER_AGENT = settings.URL_VALIDATOR_USER_AGENT |
|---|
| 418 |
except ImportError: |
|---|
| 419 |
# It's OK if Django settings aren't configured. |
|---|
| 420 |
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)' |
|---|
| 421 |
|
|---|
| 422 |
|
|---|
| 423 |
class FileField(Field): |
|---|
| 424 |
widget = FileInput |
|---|
| 425 |
default_error_messages = { |
|---|
| 426 |
'invalid': _(u"No file was submitted. Check the encoding type on the form."), |
|---|
| 427 |
'missing': _(u"No file was submitted."), |
|---|
| 428 |
'empty': _(u"The submitted file is empty."), |
|---|
| 429 |
} |
|---|
| 430 |
|
|---|
| 431 |
def __init__(self, *args, **kwargs): |
|---|
| 432 |
super(FileField, self).__init__(*args, **kwargs) |
|---|
| 433 |
|
|---|
| 434 |
def clean(self, data, initial=None): |
|---|
| 435 |
super(FileField, self).clean(initial or data) |
|---|
| 436 |
if not self.required and data in EMPTY_VALUES: |
|---|
| 437 |
return None |
|---|
| 438 |
elif not data and initial: |
|---|
| 439 |
return initial |
|---|
| 440 |
|
|---|
| 441 |
if isinstance(data, dict): |
|---|
| 442 |
# We warn once, then support both ways below. |
|---|
| 443 |
import warnings |
|---|
| 444 |
warnings.warn( |
|---|
| 445 |
message = "Representing uploaded files as dictionaries is deprecated. Use django.core.files.uploadedfile.SimpleUploadedFile instead.", |
|---|
| 446 |
category = DeprecationWarning, |
|---|
| 447 |
stacklevel = 2 |
|---|
| 448 |
) |
|---|
| 449 |
data = UploadedFile(data['filename'], data['content']) |
|---|
| 450 |
|
|---|
| 451 |
try: |
|---|
| 452 |
file_name = data.name |
|---|
| 453 |
file_size = data.size |
|---|
| 454 |
except AttributeError: |
|---|
| 455 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 456 |
|
|---|
| 457 |
if not file_name: |
|---|
| 458 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 459 |
if not file_size: |
|---|
| 460 |
raise ValidationError(self.error_messages['empty']) |
|---|
| 461 |
|
|---|
| 462 |
return data |
|---|
| 463 |
|
|---|
| 464 |
class ImageField(FileField): |
|---|
| 465 |
default_error_messages = { |
|---|
| 466 |
'invalid_image': _(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."), |
|---|
| 467 |
} |
|---|
| 468 |
|
|---|
| 469 |
def clean(self, data, initial=None): |
|---|
| 470 |
""" |
|---|
| 471 |
Checks that the file-upload field data contains a valid image (GIF, JPG, |
|---|
| 472 |
PNG, possibly others -- whatever the Python Imaging Library supports). |
|---|
| 473 |
""" |
|---|
| 474 |
f = super(ImageField, self).clean(data, initial) |
|---|
| 475 |
if f is None: |
|---|
| 476 |
return None |
|---|
| 477 |
elif not data and initial: |
|---|
| 478 |
return initial |
|---|
| 479 |
from PIL import Image |
|---|
| 480 |
|
|---|
| 481 |
# We need to get a file object for PIL. We might have a path or we might |
|---|
| 482 |
# have to read the data into memory. |
|---|
| 483 |
if hasattr(data, 'temporary_file_path'): |
|---|
| 484 |
file = data.temporary_file_path() |
|---|
| 485 |
else: |
|---|
| 486 |
if hasattr(data, 'read'): |
|---|
| 487 |
file = StringIO(data.read()) |
|---|
| 488 |
else: |
|---|
| 489 |
file = StringIO(data['content']) |
|---|
| 490 |
|
|---|
| 491 |
try: |
|---|
| 492 |
# load() is the only method that can spot a truncated JPEG, |
|---|
| 493 |
# but it cannot be called sanely after verify() |
|---|
| 494 |
trial_image = Image.open(file) |
|---|
| 495 |
|
|---|