Ticket #3297: filefield-5779.diff
File filefield-5779.diff, 18.2 KB (added by , 17 years ago) |
---|
-
django/db/models/fields/__init__.py
376 376 return self._choices 377 377 choices = property(_get_choices) 378 378 379 def save_form_data(self, instance, data): 380 setattr(instance, self.name, data) 381 379 382 def formfield(self, form_class=forms.CharField, **kwargs): 380 383 "Returns a django.newforms.Field instance for this database Field." 381 384 defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text} … … 692 695 self.upload_to = upload_to 693 696 Field.__init__(self, verbose_name, name, **kwargs) 694 697 698 def get_db_prep_save(self, value): 699 "Returns field's value prepared for saving into a database." 700 # Need to convert UploadedFile objects provided via a form to unicode for database insertion 701 return value and unicode(value) or None 702 695 703 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): 696 704 field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow) 697 705 if not self.blank: … … 768 776 f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename))) 769 777 return os.path.normpath(f) 770 778 779 def save_form_data(self, instance, data): 780 if data: 781 getattr(instance, "save_%s_file" % self.name)(os.path.join(self.upload_to, data.filename), data.content, save=False) 782 783 def formfield(self, **kwargs): 784 defaults = {'form_class': forms.FileField} 785 # If a file has been provided previously, then the form doesn't require 786 # that a new file is provided this time. 787 if 'initial' in kwargs: 788 defaults['required'] = False 789 defaults.update(kwargs) 790 return super(FileField, self).formfield(**defaults) 791 771 792 class FilePathField(Field): 772 793 def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs): 773 794 self.path, self.match, self.recursive = path, match, recursive … … 816 837 setattr(new_object, self.height_field, getattr(original_object, self.height_field)) 817 838 new_object.save() 818 839 840 def formfield(self, **kwargs): 841 defaults = {'form_class': forms.ImageField} 842 return super(ImageField, self).formfield(**defaults) 843 819 844 class IntegerField(Field): 820 845 empty_strings_allowed = False 821 846 def get_manipulator_field_objs(self): -
django/db/models/fields/related.py
756 756 "Returns the value of this field in the given model instance." 757 757 return getattr(obj, self.attname).all() 758 758 759 def save_form_data(self, instance, data): 760 setattr(instance, self.attname, data) 761 759 762 def formfield(self, **kwargs): 760 763 defaults = {'form_class': forms.ModelMultipleChoiceField, 'queryset': self.rel.to._default_manager.all()} 761 764 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
34 34 continue 35 35 if fields and f.name not in fields: 36 36 continue 37 setattr(instance, f.name, cleaned_data[f.name])37 f.save_form_data(instance, cleaned_data[f.name]) 38 38 if commit: 39 39 instance.save() 40 40 for f in opts.many_to_many: 41 41 if fields and f.name not in fields: 42 42 continue 43 43 if f.name in cleaned_data: 44 setattr(instance, f.attname, cleaned_data[f.name])44 f.save_form_data(instance, cleaned_data[f.name]) 45 45 # GOTCHA: If many-to-many data is given and commit=False, the many-to-many 46 46 # data will be lost. This happens because a many-to-many options cannot be 47 47 # 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 ugettext 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 try: 16 16 from decimal import Decimal, DecimalException … … 22 22 'DEFAULT_DATE_INPUT_FORMATS', 'DateField', 23 23 'DEFAULT_TIME_INPUT_FORMATS', 'TimeField', 24 24 'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField', 25 'RegexField', 'EmailField', ' URLField', 'BooleanField',25 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 'BooleanField', 26 26 'ChoiceField', 'NullBooleanField', 'MultipleChoiceField', 27 27 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 28 28 'SplitDateTimeField', … … 347 347 # It's OK if Django settings aren't configured. 348 348 URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)' 349 349 350 class UploadedFile(StrAndUnicode): 351 "A wrapper for files uploaded in a FileField" 352 def __init__(self, filename, content): 353 self.filename = filename 354 self.content = content 355 356 def __unicode__(self): 357 """ 358 The unicode representation is the filename, so that the pre-database-insertion 359 logic can use UploadedFile objects 360 """ 361 return self.filename 362 363 class FileField(Field): 364 widget = FileInput 365 def __init__(self, *args, **kwargs): 366 super(FileField, self).__init__(*args, **kwargs) 367 368 def clean(self, data): 369 super(FileField, self).clean(data) 370 if not self.required and data in EMPTY_VALUES: 371 return None 372 try: 373 f = UploadedFile(data['filename'], data['content']) 374 except TypeError: 375 raise ValidationError(gettext(u"No file was submitted. Check the encoding type on the form.")) 376 except KeyError: 377 raise ValidationError(gettext(u"No file was submitted.")) 378 if not f.content: 379 raise ValidationError(gettext(u"The submitted file is empty.")) 380 return f 381 382 class ImageField(FileField): 383 def clean(self, data): 384 """ 385 Checks that the file-upload field data contains a valid image (GIF, JPG, 386 PNG, possibly others -- whatever the Python Imaging Library supports). 387 """ 388 f = super(ImageField, self).clean(data) 389 if f is None: 390 return None 391 from PIL import Image 392 from cStringIO import StringIO 393 try: 394 Image.open(StringIO(f.content)) 395 except IOError: # Python Imaging Library doesn't recognize it as an image 396 raise ValidationError(gettext(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image.")) 397 return f 398 350 399 class URLField(RegexField): 351 400 def __init__(self, max_length=None, min_length=None, verify_exists=False, 352 401 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
47 47 attrs.update(extra_attrs) 48 48 return attrs 49 49 50 def value_from_datadict(self, data, name):50 def value_from_datadict(self, data, files, name): 51 51 """ 52 52 Given a dictionary of data and this widget's name, returns the value 53 53 of this widget. Returns None if it's not provided. … … 113 113 final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) 114 114 return u'\n'.join([(u'<input%s />' % flatatt(dict(value=force_unicode(v), **final_attrs))) for v in value]) 115 115 116 def value_from_datadict(self, data, name):116 def value_from_datadict(self, data, files, name): 117 117 if isinstance(data, MultiValueDict): 118 118 return data.getlist(name) 119 119 return data.get(name, None) … … 121 121 class FileInput(Input): 122 122 input_type = 'file' 123 123 124 def render(self, name, value, attrs=None): 125 return super(FileInput, self).render(name, None, attrs=attrs) 126 127 def value_from_datadict(self, data, files, name): 128 "File widgets take data from FILES, not POST" 129 return files.get(name, None) 130 124 131 class Textarea(Widget): 125 132 def __init__(self, attrs=None): 126 133 # The 'rows' and 'cols' attributes are required for HTML correctness. … … 188 195 value = u'1' 189 196 return super(NullBooleanSelect, self).render(name, value, attrs, choices) 190 197 191 def value_from_datadict(self, data, name):198 def value_from_datadict(self, data, files, name): 192 199 value = data.get(name, None) 193 200 return {u'2': True, u'3': False, True: True, False: False}.get(value, None) 194 201 … … 210 217 output.append(u'</select>') 211 218 return u'\n'.join(output) 212 219 213 def value_from_datadict(self, data, name):220 def value_from_datadict(self, data, files, name): 214 221 if isinstance(data, MultiValueDict): 215 222 return data.getlist(name) 216 223 return data.get(name, None) … … 356 363 return id_ 357 364 id_for_label = classmethod(id_for_label) 358 365 359 def value_from_datadict(self, data, name):360 return [widget.value_from_datadict(data, name + '_%s' % i) for i, widget in enumerate(self.widgets)]366 def value_from_datadict(self, data, files, name): 367 return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)] 361 368 362 369 def format_output(self, rendered_widgets): 363 370 """ -
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 … … 1520 1522 ... 1521 1523 ValidationError: [u'Ensure this value has at most 15 characters (it has 20).'] 1522 1524 1525 # FileField ################################################################## 1526 1527 >>> f = FileField() 1528 >>> f.clean('') 1529 Traceback (most recent call last): 1530 ... 1531 ValidationError: [u'This field is required.'] 1532 1533 >>> f.clean(None) 1534 Traceback (most recent call last): 1535 ... 1536 ValidationError: [u'This field is required.'] 1537 1538 >>> f.clean({}) 1539 Traceback (most recent call last): 1540 ... 1541 ValidationError: [u'No file was submitted.'] 1542 1543 >>> f.clean('some content that is not a file') 1544 Traceback (most recent call last): 1545 ... 1546 ValidationError: [u'No file was submitted. Check the encoding type on the form.'] 1547 1548 >>> f.clean({'filename': 'name', 'content':None}) 1549 Traceback (most recent call last): 1550 ... 1551 ValidationError: [u'The submitted file is empty.'] 1552 1553 >>> f.clean({'filename': 'name', 'content':''}) 1554 Traceback (most recent call last): 1555 ... 1556 ValidationError: [u'The submitted file is empty.'] 1557 1558 >>> type(f.clean({'filename': 'name', 'content':'Some File Content'})) 1559 <class 'django.newforms.fields.UploadedFile'> 1560 1523 1561 # URLField ################################################################## 1524 1562 1525 1563 >>> f = URLField() … … 2561 2599 the next. 2562 2600 >>> class MyForm(Form): 2563 2601 ... def __init__(self, data=None, auto_id=False, field_list=[]): 2564 ... Form.__init__(self, data, auto_id )2602 ... Form.__init__(self, data, auto_id=auto_id) 2565 2603 ... for field in field_list: 2566 2604 ... self.fields[field[0]] = field[1] 2567 2605 >>> field_list = [('field1', CharField()), ('field2', CharField())] … … 2579 2617 ... default_field_1 = CharField() 2580 2618 ... default_field_2 = CharField() 2581 2619 ... def __init__(self, data=None, auto_id=False, field_list=[]): 2582 ... Form.__init__(self, data, auto_id )2620 ... Form.__init__(self, data, auto_id=auto_id) 2583 2621 ... for field in field_list: 2584 2622 ... self.fields[field[0]] = field[1] 2585 2623 >>> field_list = [('field1', CharField()), ('field2', CharField())] … … 3234 3272 <option value="3" selected="selected">No</option> 3235 3273 </select> 3236 3274 3275 # Forms with FileFields ################################################ 3276 3277 FileFields are a special case because they take their data from the request.FILES, 3278 not request.POST. 3279 3280 >>> class FileForm(Form): 3281 ... file1 = FileField() 3282 >>> f = FileForm(auto_id=False) 3283 >>> print f 3284 <tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> 3285 3286 >>> f = FileForm(data={}, files={}, auto_id=False) 3287 >>> print f 3288 <tr><th>File1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="file" name="file1" /></td></tr> 3289 3290 >>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':''}}, auto_id=False) 3291 >>> print f 3292 <tr><th>File1:</th><td><ul class="errorlist"><li>The submitted file is empty.</li></ul><input type="file" name="file1" /></td></tr> 3293 3294 >>> f = FileForm(data={}, files={'file1': 'something that is not a file'}, auto_id=False) 3295 >>> print f 3296 <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> 3297 3298 >>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':'some content'}}, auto_id=False) 3299 >>> print f 3300 <tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> 3301 >>> f.is_valid() 3302 True 3303 3237 3304 # Basic form processing in a view ############################################# 3238 3305 3239 3306 >>> from django.template import Template, Context