Ticket #3297: filefield_with_newforms_admin_integration_cleaner_updated_and_corrected.2.diff
File filefield_with_newforms_admin_integration_cleaner_updated_and_corrected.2.diff, 20.3 KB (added by , 17 years ago) |
---|
-
django/db/models/fields/__init__.py
342 342 return self._choices 343 343 choices = property(_get_choices) 344 344 345 def save_form_data(self, instance, data): 346 setattr(instance, self.name, data) 347 345 348 def formfield(self, form_class=forms.CharField, **kwargs): 346 349 "Returns a django.newforms.Field instance for this database Field." 347 350 defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text} … … 657 660 self.upload_to = upload_to 658 661 Field.__init__(self, verbose_name, name, **kwargs) 659 662 663 def get_db_prep_save(self, value): 664 "Returns field's value prepared for saving into a database." 665 # Need to convert UploadedFile objects provided via a form to unicode for database insertion 666 return value and unicode(value) or None 667 660 668 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): 661 669 field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow) 662 670 if not self.blank: … … 733 741 f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename))) 734 742 return os.path.normpath(f) 735 743 744 def save_form_data(self, instance, data): 745 if data: 746 getattr(instance, "save_%s_file" % self.name)(os.path.join(self.upload_to, data.filename), data.content, save=False) 747 748 def formfield(self, **kwargs): 749 defaults = {'form_class': forms.FileField} 750 # If a file has been provided previously, then the form doesn't require 751 # that a new file is provided this time. 752 if 'initial' in kwargs: 753 defaults['required'] = False 754 defaults.update(kwargs) 755 return super(FileField, self).formfield(**defaults) 756 736 757 class FilePathField(Field): 737 758 def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs): 738 759 self.path, self.match, self.recursive = path, match, recursive … … 781 802 setattr(new_object, self.height_field, getattr(original_object, self.height_field)) 782 803 new_object.save() 783 804 805 def formfield(self, **kwargs): 806 defaults = {'form_class': forms.ImageField} 807 return super(ImageField, self).formfield(**defaults) 808 784 809 class IntegerField(Field): 785 810 empty_strings_allowed = False 786 811 def get_manipulator_field_objs(self): -
django/db/models/fields/related.py
711 711 "Returns the value of this field in the given model instance." 712 712 return getattr(obj, self.attname).all() 713 713 714 def save_form_data(self, instance, data): 715 setattr(instance, self.attname, data) 716 714 717 def formfield(self, **kwargs): 715 718 defaults = {'form_class': forms.ModelMultipleChoiceField, 'queryset': self.rel.to._default_manager.all()} 716 719 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 if f is None: 394 return None 395 from PIL import Image 396 from cStringIO import StringIO 397 try: 398 Image.open(StringIO(f.content)) 399 except IOError: # Python Imaging Library doesn't recognize it as an image 400 raise ValidationError(gettext(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image.")) 401 return f 402 354 403 class URLField(RegexField): 355 404 def __init__(self, max_length=None, min_length=None, verify_exists=False, 356 405 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 … … 198 199 return 199 200 self.cleaned_data = {} 200 201 for name, field in self.fields.items(): 201 # value_from_datadict() gets the data from the d ictionary.202 # value_from_datadict() gets the data from the data dictionaries. 202 203 # Each widget type knows how to retrieve its own data, because some 203 204 # widgets split data over several HTML fields. 204 value = field.widget.value_from_datadict(self.data, self. add_prefix(name))205 value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) 205 206 try: 206 207 value = field.clean(value) 207 208 self.cleaned_data[name] = value … … 309 310 """ 310 311 Returns the data for this BoundField, or None if it wasn't given. 311 312 """ 312 return self.field.widget.value_from_datadict(self.form.data, self. html_name)313 return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name) 313 314 data = property(_data) 314 315 315 316 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) -
django/contrib/admin/options.py
416 416 417 417 inline_formsets = [] 418 418 if request.POST: 419 new_data = request.POST.copy() 420 if opts.has_field_type(models.FileField): 421 new_data.update(request.FILES) 422 form = ModelForm(new_data) 419 form = ModelForm(request.POST, request.FILES) 423 420 for FormSet in self.get_inline_formsets(): 424 421 inline_formset = FormSet(data=new_data) 425 422 inline_formsets.append(inline_formset) … … 469 466 470 467 inline_formsets = [] 471 468 if request.POST: 472 new_data = request.POST.copy() 473 if opts.has_field_type(models.FileField): 474 new_data.update(request.FILES) 475 form = ModelForm(new_data) 469 form = ModelForm(request.POST, request.FILES) 476 470 for FormSet in self.get_inline_formsets(): 477 471 inline_formset = FormSet(obj, new_data) 478 472 inline_formsets.append(inline_formset) -
django/contrib/admin/urls.py
1 1 from django.conf.urls.defaults import * 2 2 3 3 urlpatterns = patterns('', 4 #('^$', 'django.contrib.admin.views.main.index'),4 ('^$', 'django.contrib.admin.views.main.index'), 5 5 ('^r/(\d+)/(.*)/$', 'django.views.defaults.shortcut'), 6 6 #('^jsi18n/$', i18n_view, {'packages': 'django.conf'}), 7 #('^logout/$', 'django.contrib.auth.views.logout'),7 ('^logout/$', 'django.contrib.auth.views.logout'), 8 8 #('^password_change/$', 'django.contrib.auth.views.password_change'), 9 9 #('^password_change/done/$', 'django.contrib.auth.views.password_change_done'), 10 10 ('^template_validator/$', 'django.contrib.admin.views.template.template_validator'), -
tests/regressiontests/forms/tests.py
173 173 174 174 # FileInput Widget ############################################################ 175 175 176 FileInput widgets don't ever show the value, because the old value is of no use 177 if you are updating the form or if the provided file generated an error. 176 178 >>> w = FileInput() 177 179 >>> w.render('email', '') 178 180 u'<input type="file" name="email" />' 179 181 >>> w.render('email', None) 180 182 u'<input type="file" name="email" />' 181 183 >>> w.render('email', 'test@example.com') 182 u'<input type="file" name="email" value="test@example.com"/>'184 u'<input type="file" name="email" />' 183 185 >>> w.render('email', 'some "quoted" & ampersanded value') 184 u'<input type="file" name="email" value="some "quoted" & ampersanded value"/>'186 u'<input type="file" name="email" />' 185 187 >>> w.render('email', 'test@example.com', attrs={'class': 'fun'}) 186 u'<input type="file" name="email" value="test@example.com"class="fun" />'188 u'<input type="file" name="email" class="fun" />' 187 189 188 190 You can also pass 'attrs' to the constructor: 189 191 >>> w = FileInput(attrs={'class': 'fun'}) 190 192 >>> w.render('email', '') 191 193 u'<input type="file" class="fun" name="email" />' 192 194 >>> w.render('email', 'foo@example.com') 193 u'<input type="file" class="fun" value="foo@example.com"name="email" />'195 u'<input type="file" class="fun" name="email" />' 194 196 195 197 >>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) 196 u'<input type="file" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111"name="email" />'198 u'<input type="file" class="fun" name="email" />' 197 199 198 200 # Textarea Widget ############################################################# 199 201 … … 1517 1519 ... 1518 1520 ValidationError: [u'Ensure this value has at most 15 characters.'] 1519 1521 1522 # FileField ################################################################## 1523 1524 >>> f = FileField() 1525 >>> f.clean('') 1526 Traceback (most recent call last): 1527 ... 1528 ValidationError: [u'This field is required.'] 1529 1530 >>> f.clean(None) 1531 Traceback (most recent call last): 1532 ... 1533 ValidationError: [u'This field is required.'] 1534 1535 >>> f.clean({}) 1536 Traceback (most recent call last): 1537 ... 1538 ValidationError: [u'No file was submitted.'] 1539 1540 >>> f.clean('some content that is not a file') 1541 Traceback (most recent call last): 1542 ... 1543 ValidationError: [u'No file was submitted. Check the encoding type on the form.'] 1544 1545 >>> f.clean({'filename': 'name', 'content':None}) 1546 Traceback (most recent call last): 1547 ... 1548 ValidationError: [u'The submitted file is empty.'] 1549 1550 >>> f.clean({'filename': 'name', 'content':''}) 1551 Traceback (most recent call last): 1552 ... 1553 ValidationError: [u'The submitted file is empty.'] 1554 1555 >>> type(f.clean({'filename': 'name', 'content':'Some File Content'})) 1556 <class 'django.newforms.fields.UploadedFile'> 1557 1520 1558 # URLField ################################################################## 1521 1559 1522 1560 >>> f = URLField() … … 2542 2580 the next. 2543 2581 >>> class MyForm(Form): 2544 2582 ... def __init__(self, data=None, auto_id=False, field_list=[]): 2545 ... Form.__init__(self, data, auto_id )2583 ... Form.__init__(self, data, auto_id=auto_id) 2546 2584 ... for field in field_list: 2547 2585 ... self.fields[field[0]] = field[1] 2548 2586 >>> field_list = [('field1', CharField()), ('field2', CharField())] … … 2560 2598 ... default_field_1 = CharField() 2561 2599 ... default_field_2 = CharField() 2562 2600 ... def __init__(self, data=None, auto_id=False, field_list=[]): 2563 ... Form.__init__(self, data, auto_id )2601 ... Form.__init__(self, data, auto_id=auto_id) 2564 2602 ... for field in field_list: 2565 2603 ... self.fields[field[0]] = field[1] 2566 2604 >>> field_list = [('field1', CharField()), ('field2', CharField())] … … 3215 3253 <option value="3" selected="selected">No</option> 3216 3254 </select> 3217 3255 3256 # Forms with FileFields ################################################ 3257 3258 FileFields are a special case because they take their data from the request.FILES, 3259 not request.POST. 3260 3261 >>> class FileForm(Form): 3262 ... file1 = FileField() 3263 >>> f = FileForm(auto_id=False) 3264 >>> print f 3265 <tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> 3266 3267 >>> f = FileForm(data={}, files={}, auto_id=False) 3268 >>> print f 3269 <tr><th>File1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="file" name="file1" /></td></tr> 3270 3271 >>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':''}}, auto_id=False) 3272 >>> print f 3273 <tr><th>File1:</th><td><ul class="errorlist"><li>The submitted file is empty.</li></ul><input type="file" name="file1" /></td></tr> 3274 3275 >>> f = FileForm(data={}, files={'file1': 'something that is not a file'}, auto_id=False) 3276 >>> print f 3277 <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> 3278 3279 >>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':'some content'}}, auto_id=False) 3280 >>> print f 3281 <tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> 3282 >>> f.is_valid() 3283 True 3284 3218 3285 # Basic form processing in a view ############################################# 3219 3286 3220 3287 >>> from django.template import Template, Context