Ticket #3297: filefield_with_newforms_admin_integration_cleaner.diff
File filefield_with_newforms_admin_integration_cleaner.diff, 20.2 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 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 … … 197 198 return 198 199 self.cleaned_data = {} 199 200 for name, field in self.fields.items(): 200 # value_from_datadict() gets the data from the d ictionary.201 # value_from_datadict() gets the data from the data dictionaries. 201 202 # Each widget type knows how to retrieve its own data, because some 202 203 # widgets split data over several HTML fields. 203 value = field.widget.value_from_datadict(self.data, self. add_prefix(name))204 value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) 204 205 try: 205 206 value = field.clean(value) 206 207 self.cleaned_data[name] = value … … 308 309 """ 309 310 Returns the data for this BoundField, or None if it wasn't given. 310 311 """ 311 return self.field.widget.value_from_datadict(self.form.data, self. html_name)312 return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name) 312 313 data = property(_data) 313 314 314 315 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
395 395 ModelForm = forms.form_for_model(model, formfield_callback=self.formfield_for_dbfield) 396 396 397 397 if request.POST: 398 new_data = request.POST.copy() 399 if opts.has_field_type(models.FileField): 400 new_data.update(request.FILES) 401 form = ModelForm(new_data) 398 form = ModelForm(request.POST, request.FILES) 402 399 if form.is_valid(): 403 400 return self.save_add(request, model, form, '../%s/') 404 401 else: … … 440 437 ModelForm = forms.form_for_instance(obj, formfield_callback=self.formfield_for_dbfield) 441 438 442 439 if request.POST: 443 new_data = request.POST.copy() 444 if opts.has_field_type(models.FileField): 445 new_data.update(request.FILES) 446 form = ModelForm(new_data) 447 440 form = ModelForm(request.POST, request.FILES) 448 441 if form.is_valid(): 449 442 return self.save_change(request, model, form) 450 443 else: -
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