Ticket #3297: filefield.diff
File filefield.diff, 18.1 KB (added by , 17 years ago) |
---|
-
django/db/models/fields/__init__.py
345 345 return self._choices 346 346 choices = property(_get_choices) 347 347 348 def save_form_data(self, instance, data): 349 setattr(instance, self.name, data) 350 348 351 def formfield(self, form_class=forms.CharField, **kwargs): 349 352 "Returns a django.newforms.Field instance for this database Field." 350 353 defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text} … … 660 663 self.upload_to = upload_to 661 664 Field.__init__(self, verbose_name, name, **kwargs) 662 665 666 def get_db_prep_save(self, value): 667 "Returns field's value prepared for saving into a database." 668 # Need to convert UploadedFile objects provided via a form to unicode for database insertion 669 return value and unicode(value) or None 670 663 671 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): 664 672 field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow) 665 673 if not self.blank: … … 736 744 f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename))) 737 745 return os.path.normpath(f) 738 746 747 def save_form_data(self, instance, data): 748 if data: 749 getattr(instance, "save_%s_file" % self.name)(os.path.join(self.upload_to, data.filename), data.content) 750 751 def formfield(self, **kwargs): 752 defaults = {'form_class': forms.FileField} 753 # If a file has been provided previously, then the form doesn't require 754 # that a new file is provided this time. 755 if 'initial' in kwargs: 756 defaults['required'] = False 757 defaults.update(kwargs) 758 return super(FileField, self).formfield(**defaults) 759 739 760 class FilePathField(Field): 740 761 def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs): 741 762 self.path, self.match, self.recursive = path, match, recursive … … 784 805 setattr(new_object, self.height_field, getattr(original_object, self.height_field)) 785 806 new_object.save() 786 807 808 def formfield(self, **kwargs): 809 defaults = {'form_class': forms.ImageField} 810 return super(ImageField, self).formfield(**defaults) 811 787 812 class IntegerField(Field): 788 813 empty_strings_allowed = False 789 814 def get_manipulator_field_objs(self): -
django/db/models/fields/related.py
737 737 "Returns the value of this field in the given model instance." 738 738 return getattr(obj, self.attname).all() 739 739 740 def save_form_data(self, instance, data): 741 setattr(instance, self.attname, data) 742 740 743 def formfield(self, **kwargs): 741 744 defaults = {'form_class': forms.ModelMultipleChoiceField, 'queryset': self.rel.to._default_manager.all()} 742 745 defaults.update(kwargs) -
django/newforms/extras/widgets.py
53 53 54 54 return u'\n'.join(output) 55 55 56 def value_from_datadict(self, data, name):56 def value_from_datadict(self, data, files, name): 57 57 y, m, d = data.get(self.year_field % name), data.get(self.month_field % name), data.get(self.day_field % name) 58 58 if y and m and d: 59 59 return '%s-%s-%s' % (y, m, d) -
django/newforms/models.py
33 33 continue 34 34 if fields and f.name not in fields: 35 35 continue 36 setattr(instance, f.name, cleaned_data[f.name])36 f.save_form_data(instance, cleaned_data[f.name]) 37 37 if commit: 38 38 instance.save() 39 39 for f in opts.many_to_many: 40 40 if fields and f.name not in fields: 41 41 continue 42 42 if f.name in cleaned_data: 43 setattr(instance, f.attname, cleaned_data[f.name])43 f.save_form_data(instance, cleaned_data[f.name]) 44 44 # GOTCHA: If many-to-many data is given and commit=False, the many-to-many 45 45 # data will be lost. This happens because a many-to-many options cannot be 46 46 # set on an object until after it's saved. Maybe we should raise an -
django/newforms/fields.py
7 7 import time 8 8 9 9 from django.utils.translation import gettext 10 from django.utils.encoding import smart_unicode10 from django.utils.encoding import StrAndUnicode, smart_unicode 11 11 12 12 from util import ErrorList, ValidationError 13 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple13 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple 14 14 15 15 __all__ = ( 16 16 'Field', 'CharField', 'IntegerField', 17 17 'DEFAULT_DATE_INPUT_FORMATS', 'DateField', 18 18 'DEFAULT_TIME_INPUT_FORMATS', 'TimeField', 19 19 'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField', 20 'RegexField', 'EmailField', ' URLField', 'BooleanField',20 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 'BooleanField', 21 21 'ChoiceField', 'NullBooleanField', 'MultipleChoiceField', 22 22 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 23 23 'SplitDateTimeField', … … 351 351 # It's OK if Django settings aren't configured. 352 352 URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)' 353 353 354 class UploadedFile(StrAndUnicode): 355 "A wrapper for files uploaded in a FileField" 356 def __init__(self, filename, content): 357 self.filename = filename 358 self.content = content 359 360 def __unicode__(self): 361 """ 362 The unicode representation is the filename, so that the pre-database-insertion 363 logic can use UploadedFile objects 364 """ 365 return self.filename 366 367 class FileField(Field): 368 widget = FileInput 369 def __init__(self, *args, **kwargs): 370 super(FileField, self).__init__(*args, **kwargs) 371 372 def clean(self, data): 373 super(FileField, self).clean(data) 374 if not self.required and data in EMPTY_VALUES: 375 return None 376 try: 377 f = UploadedFile(data['filename'], data['content']) 378 except TypeError: 379 raise ValidationError(gettext(u"No file was submitted. Check the encoding type on the form.")) 380 except KeyError: 381 raise ValidationError(gettext(u"No file was submitted.")) 382 if not f.content: 383 raise ValidationError(gettext(u"The submitted file is empty.")) 384 return f 385 386 class ImageField(FileField): 387 def clean(self, data): 388 """ 389 Checks that the file-upload field data contains a valid image (GIF, JPG, 390 PNG, possibly others -- whatever the Python Imaging Library supports). 391 """ 392 f = super(ImageField, self).clean(data) 393 from PIL import Image 394 from cStringIO import StringIO 395 try: 396 Image.open(StringIO(f.content)) 397 except IOError: # Python Imaging Library doesn't recognize it as an image 398 raise ValidationError(gettext(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image.")) 399 return f 400 354 401 class URLField(RegexField): 355 402 def __init__(self, max_length=None, min_length=None, verify_exists=False, 356 403 validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs): -
django/newforms/forms.py
57 57 # class is different than Form. See the comments by the Form class for more 58 58 # information. Any improvements to the form API should be made to *this* 59 59 # class, not to the Form class. 60 def __init__(self, data=None, auto_id='id_%s', prefix=None, initial=None):61 self.is_bound = data is not None 60 def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None): 61 self.is_bound = data is not None or files is not None 62 62 self.data = data or {} 63 self.files = files or {} 63 64 self.auto_id = auto_id 64 65 self.prefix = prefix 65 66 self.initial = initial or {} … … 88 89 return BoundField(self, field, name) 89 90 90 91 def _get_errors(self): 91 "Returns an ErrorDict for self.data"92 "Returns an ErrorDict for the data provided for the form" 92 93 if self._errors is None: 93 94 self.full_clean() 94 95 return self._errors … … 179 180 return 180 181 self.cleaned_data = {} 181 182 for name, field in self.fields.items(): 182 # value_from_datadict() gets the data from the d ictionary.183 # value_from_datadict() gets the data from the data dictionaries. 183 184 # Each widget type knows how to retrieve its own data, because some 184 185 # widgets split data over several HTML fields. 185 value = field.widget.value_from_datadict(self.data, self. add_prefix(name))186 value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) 186 187 try: 187 188 value = field.clean(value) 188 189 self.cleaned_data[name] = value … … 284 285 """ 285 286 Returns the data for this BoundField, or None if it wasn't given. 286 287 """ 287 return self.field.widget.value_from_datadict(self.form.data, self. html_name)288 return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name) 288 289 data = property(_data) 289 290 290 291 def label_tag(self, contents=None, attrs=None): -
django/newforms/widgets.py
48 48 attrs.update(extra_attrs) 49 49 return attrs 50 50 51 def value_from_datadict(self, data, name):51 def value_from_datadict(self, data, files, name): 52 52 """ 53 53 Given a dictionary of data and this widget's name, returns the value 54 54 of this widget. Returns None if it's not provided. … … 114 114 final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) 115 115 return u'\n'.join([(u'<input%s />' % flatatt(dict(value=smart_unicode(v), **final_attrs))) for v in value]) 116 116 117 def value_from_datadict(self, data, name):117 def value_from_datadict(self, data, files, name): 118 118 if isinstance(data, MultiValueDict): 119 119 return data.getlist(name) 120 120 return data.get(name, None) … … 122 122 class FileInput(Input): 123 123 input_type = 'file' 124 124 125 def render(self, name, value, attrs=None): 126 return super(FileInput, self).render(name, None, attrs=attrs) 127 128 def value_from_datadict(self, data, files, name): 129 "File widgets take data from FILES, not POST" 130 return files.get(name, None) 131 125 132 class Textarea(Widget): 126 133 def __init__(self, attrs=None): 127 134 # The 'rows' and 'cols' attributes are required for HTML correctness. … … 189 196 value = u'1' 190 197 return super(NullBooleanSelect, self).render(name, value, attrs, choices) 191 198 192 def value_from_datadict(self, data, name):199 def value_from_datadict(self, data, files, name): 193 200 value = data.get(name, None) 194 201 return {u'2': True, u'3': False, True: True, False: False}.get(value, None) 195 202 … … 211 218 output.append(u'</select>') 212 219 return u'\n'.join(output) 213 220 214 def value_from_datadict(self, data, name):221 def value_from_datadict(self, data, files, name): 215 222 if isinstance(data, MultiValueDict): 216 223 return data.getlist(name) 217 224 return data.get(name, None) … … 348 355 return id_ 349 356 id_for_label = classmethod(id_for_label) 350 357 351 def value_from_datadict(self, data, name):352 return [widget.value_from_datadict(data, name + '_%s' % i) for i, widget in enumerate(self.widgets)]358 def value_from_datadict(self, data, files, name): 359 return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)] 353 360 354 361 def format_output(self, rendered_widgets): 355 362 return u''.join(rendered_widgets) -
tests/regressiontests/forms/tests.py
172 172 173 173 # FileInput Widget ############################################################ 174 174 175 FileInput widgets don't ever show the value, because the old value is of no use 176 if you are updating the form or if the provided file generated an error. 175 177 >>> w = FileInput() 176 178 >>> w.render('email', '') 177 179 u'<input type="file" name="email" />' 178 180 >>> w.render('email', None) 179 181 u'<input type="file" name="email" />' 180 182 >>> w.render('email', 'test@example.com') 181 u'<input type="file" name="email" value="test@example.com"/>'183 u'<input type="file" name="email" />' 182 184 >>> w.render('email', 'some "quoted" & ampersanded value') 183 u'<input type="file" name="email" value="some "quoted" & ampersanded value"/>'185 u'<input type="file" name="email" />' 184 186 >>> w.render('email', 'test@example.com', attrs={'class': 'fun'}) 185 u'<input type="file" name="email" value="test@example.com"class="fun" />'187 u'<input type="file" name="email" class="fun" />' 186 188 187 189 You can also pass 'attrs' to the constructor: 188 190 >>> w = FileInput(attrs={'class': 'fun'}) 189 191 >>> w.render('email', '') 190 192 u'<input type="file" class="fun" name="email" />' 191 193 >>> w.render('email', 'foo@example.com') 192 u'<input type="file" class="fun" value="foo@example.com"name="email" />'194 u'<input type="file" class="fun" name="email" />' 193 195 194 196 >>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) 195 u'<input type="file" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111"name="email" />'197 u'<input type="file" class="fun" name="email" />' 196 198 197 199 # Textarea Widget ############################################################# 198 200 … … 1516 1518 ... 1517 1519 ValidationError: [u'Ensure this value has at most 15 characters.'] 1518 1520 1521 # FileField ################################################################## 1522 1523 >>> f = FileField() 1524 >>> f.clean('') 1525 Traceback (most recent call last): 1526 ... 1527 ValidationError: [u'This field is required.'] 1528 1529 >>> f.clean(None) 1530 Traceback (most recent call last): 1531 ... 1532 ValidationError: [u'This field is required.'] 1533 1534 >>> f.clean({}) 1535 Traceback (most recent call last): 1536 ... 1537 ValidationError: [u'No file was submitted.'] 1538 1539 >>> f.clean('some content that is not a file') 1540 Traceback (most recent call last): 1541 ... 1542 ValidationError: [u'No file was submitted. Check the encoding type on the form.'] 1543 1544 >>> f.clean({'filename': 'name', 'content':None}) 1545 Traceback (most recent call last): 1546 ... 1547 ValidationError: [u'The submitted file is empty.'] 1548 1549 >>> f.clean({'filename': 'name', 'content':''}) 1550 Traceback (most recent call last): 1551 ... 1552 ValidationError: [u'The submitted file is empty.'] 1553 1554 >>> type(f.clean({'filename': 'name', 'content':'Some File Content'})) 1555 <class 'django.newforms.fields.UploadedFile'> 1556 1519 1557 # URLField ################################################################## 1520 1558 1521 1559 >>> f = URLField() … … 2541 2579 the next. 2542 2580 >>> class MyForm(Form): 2543 2581 ... def __init__(self, data=None, auto_id=False, field_list=[]): 2544 ... Form.__init__(self, data, auto_id )2582 ... Form.__init__(self, data, auto_id=auto_id) 2545 2583 ... for field in field_list: 2546 2584 ... self.fields[field[0]] = field[1] 2547 2585 >>> field_list = [('field1', CharField()), ('field2', CharField())] … … 2559 2597 ... default_field_1 = CharField() 2560 2598 ... default_field_2 = CharField() 2561 2599 ... def __init__(self, data=None, auto_id=False, field_list=[]): 2562 ... Form.__init__(self, data, auto_id )2600 ... Form.__init__(self, data, auto_id=auto_id) 2563 2601 ... for field in field_list: 2564 2602 ... self.fields[field[0]] = field[1] 2565 2603 >>> field_list = [('field1', CharField()), ('field2', CharField())] … … 3214 3252 <option value="3" selected="selected">No</option> 3215 3253 </select> 3216 3254 3255 # Forms with FileFields ################################################ 3256 3257 FileFields are a special case because they take their data from the request.FILES, 3258 not request.POST. 3259 3260 >>> class FileForm(Form): 3261 ... file1 = FileField() 3262 >>> f = FileForm(auto_id=False) 3263 >>> print f 3264 <tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> 3265 3266 >>> f = FileForm(data={}, files={}, auto_id=False) 3267 >>> print f 3268 <tr><th>File1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="file" name="file1" /></td></tr> 3269 3270 >>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':''}}, auto_id=False) 3271 >>> print f 3272 <tr><th>File1:</th><td><ul class="errorlist"><li>The submitted file is empty.</li></ul><input type="file" name="file1" /></td></tr> 3273 3274 >>> f = FileForm(data={}, files={'file1': 'something that is not a file'}, auto_id=False) 3275 >>> print f 3276 <tr><th>File1:</th><td><ul class="errorlist"><li>No file was submitted. Check the encoding type on the form.</li></ul><input type="file" name="file1" /></td></tr> 3277 3278 >>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':'some content'}}, auto_id=False) 3279 >>> print f 3280 <tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> 3281 >>> f.is_valid() 3282 True 3283 3217 3284 # Basic form processing in a view ############################################# 3218 3285 3219 3286 >>> from django.template import Template, Context