Ticket #3297: 4921-newforms-file-imagefield.diff

File 4921-newforms-file-imagefield.diff, 11.5 KB (added by David Danier <goliath.mailinglist@…>, 12 years ago)

Patch without additional hidden field for current trunk (rev 4921)

  • django/db/models/fields/__init__.py

    ==== Patch <django-newforms-file-imagefield> level 2
    Source: 6666b74b-a72c-0410-9b74-dd6beae1e16f:/django/newforms-file-imagefield:5816 [local]
    Target: bcc190cf-cafb-0310-a4f2-bffc1f526a37:/django/trunk:4921 [mirrored]
            (http://code.djangoproject.com/svn/django/trunk)
    Log:
     r5600@damasonium:  ddanier | 2007-03-27 16:06:05 +0200
      * FileField and ImageField for newforms not using additional <input>'s
     r5808@damasonium:  ddanier | 2007-04-04 10:08:34 +0200
      * FileInput is now the default-widget for FileField
    
    === django/db/models/fields/__init__.py
    ==================================================================
     
    663663            else:
    664664                func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"], save)
    665665
     666    def formfield_save_file(self, field_name, new_data, new_object, save=True):
     667        if new_data[field_name]:
     668            func = getattr(new_object, 'save_%s_file' % self.name)
     669            func(new_data[field_name]["filename"], new_data[field_name]["content"], save)
     670
    666671    def get_directory_name(self):
    667672        return os.path.normpath(datetime.datetime.now().strftime(self.upload_to))
    668673
     
    671676        f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))
    672677        return os.path.normpath(f)
    673678
     679    def formfield(self, **kwargs):
     680        defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'widget': forms.FileInput}
     681        defaults.update(kwargs)
     682        return forms.FileField(**defaults)
     683
    674684class FilePathField(Field):
    675685    def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
    676686        self.path, self.match, self.recursive = path, match, recursive
     
    717727                setattr(new_object, self.height_field, getattr(original_object, self.height_field))
    718728            new_object.save()
    719729
     730    def formfield(self, **kwargs):
     731        defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'widget': forms.FileInput}
     732        defaults.update(kwargs)
     733        return forms.ImageField(**defaults)
     734
    720735class IntegerField(Field):
    721736    empty_strings_allowed = False
    722737    def get_manipulator_field_objs(self):
  • django/newforms/models.py

    === django/newforms/models.py
    ==================================================================
     
    1818
    1919    This method is created for any form_for_model Form.
    2020    """
     21    from django.db import models
    2122    if self.errors:
    2223        raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name)
    2324    return save_instance(self, self._model(), commit)
     
    3637        raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
    3738    clean_data = form.clean_data
    3839    for f in opts.fields:
    39         if not f.editable or isinstance(f, models.AutoField) or not f.name in clean_data:
    40             continue
    41         setattr(instance, f.name, clean_data[f.name])
     40        if clean_data.has_key(f.name):
     41            if not f.editable or isinstance(f, models.AutoField):
     42                continue
     43            if isinstance(f, models.FileField):
     44                continue
     45            setattr(instance, f.name, clean_data[f.name])
     46
     47    # FileField may need more info to save to specific paths
     48    for f in opts.fields:
     49        if isinstance(f, models.FileField) and clean_data.has_key(f.name):
     50            f.formfield_save_file(f.name, clean_data, instance, save=False)
     51
    4252    if commit:
    4353        instance.save()
    4454        for f in opts.many_to_many:
     
    8898    takes a database Field instance, plus **kwargs, and returns a form Field
    8999    instance with the given kwargs (i.e. 'initial').
    90100    """
     101    from django.db import models
    91102    model = instance.__class__
    92103    opts = model._meta
    93104    field_list = []
     
    97108        current_value = f.value_from_object(instance)
    98109        formfield = formfield_callback(f, initial=current_value)
    99110        if formfield:
     111            # FileFields are only required once, because we cannot print the
     112            # existing data into the <form>
     113            if isinstance(f, models.FileField) and current_value:
     114                formfield.required = False
    100115            field_list.append((f.name, formfield))
    101116    fields = SortedDictFromList(field_list)
    102117    return type(opts.object_name + 'InstanceForm', (form,),
  • django/newforms/fields.py

    === django/newforms/fields.py
    ==================================================================
     
    55from django.utils.translation import gettext
    66from django.utils.encoding import smart_unicode
    77from util import ErrorList, ValidationError
    8 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple
     8from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, FileInput
    99import datetime
    1010import re
    1111import time
    1212
    1313__all__ = (
    14     'Field', 'CharField', 'IntegerField',
     14    'Field', 'CharField', 'FileField', 'ImageField', 'IntegerField',
    1515    'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
    1616    'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
    1717    'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
     
    109109        if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
    110110            return {'maxlength': str(self.max_length)}
    111111
     112class FileField(Field):
     113    widget = FileInput
     114   
     115    def clean(self, value):
     116        super(FileField, self).clean(value)
     117        if value in EMPTY_VALUES:
     118            return None
     119        else:
     120            try:
     121                content = value['content']
     122            except TypeError:
     123                raise ValidationError(gettext("No file was submitted. Check the encoding type on the form."))
     124            if not content:
     125                raise ValidationError(gettext(u'The submitted file is empty.'))
     126            return value
     127
     128class ImageField(FileField):
     129    def clean(self, value):
     130        super(ImageField, self).clean(value)
     131        if value in EMPTY_VALUES:
     132            return None
     133        else:
     134            from PIL import Image
     135            from cStringIO import StringIO
     136            try:
     137                Image.open(StringIO(value['content']))
     138            except IOError: # Python Imaging Library doesn't recognize it as an image
     139                raise ValidationError, gettext(u'Upload a valid image. The file you uploaded was either not an image or a corrupted image.')
     140            return value
     141
    112142class IntegerField(Field):
    113143    def __init__(self, max_value=None, min_value=None, *args, **kwargs):
    114144        self.max_value, self.min_value = max_value, min_value
  • tests/modeltests/model_forms/models.py

    === tests/modeltests/model_forms/models.py
    ==================================================================
     
    6161    def __str__(self):
    6262        return self.phone
    6363
     64class NotRequiredFile(models.Model):
     65    description = models.CharField(maxlength=50)
     66    file = models.FileField(upload_to='files', blank=True)
     67
     68    def __str__(self):
     69        return self.description
     70
     71class RequiredFile(models.Model):
     72    description = models.CharField(maxlength=50)
     73    file = models.FileField(upload_to='files')
     74
     75    def __str__(self):
     76        return self.description
     77
    6478__test__ = {'API_TESTS': """
    6579>>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField
    6680>>> import datetime
     
    377391
    378392>>> f = ModelChoiceField(Category.objects.filter(pk=1), required=False)
    379393>>> print f.clean('')
     394
     395Test for filefield
     396
     397>>> form_data = {'description': 'FileField test', 'file': {'content': 'FileField test', 'filename': 'filetest.txt'} }
     398>>> no_file_form_data = {'description': 'FileField test'}
     399>>> bad_encoding_form_data = {'description': 'FileField test', 'file': 'wrong encoding'}
     400>>> TestRequiredFileForm = form_for_model(RequiredFile)
     401>>> empty_required_fileform = TestRequiredFileForm(auto_id=False)
     402>>> print empty_required_fileform.as_ul()
     403<li>Description: <input type="text" name="description" maxlength="50" /></li>
     404<li>File: <input type="file" name="file" /></li>
     405
     406Test with data
     407>>> filled_required_fileform= TestRequiredFileForm(form_data, auto_id=False)
     408>>> filled_required_fileform.is_valid()
     409True
     410
     411>>> required_file_instance = filled_required_fileform.save()
     412>>> print required_file_instance.file
     413files/filetest.txt
     414>>> TestInstanceRequiredFileForm = form_for_instance(required_file_instance)
     415>>> instance_required_fileform = TestInstanceRequiredFileForm(auto_id=False)
     416>>> instance_required_fileform.as_ul()
     417u'<li>Description: <input type="text" name="description" value="FileField test" maxlength="50" /></li>\\n<li>File: <input type="file" name="file" /></li>'
     418>>> required_file_instance.delete()
     419
     420Bad data test
     421>>> filled_required_fileform=TestRequiredFileForm(no_file_form_data)
     422>>> filled_required_fileform.is_valid()
     423False
     424>>> print filled_required_fileform.errors
     425<ul class="errorlist"><li>file<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
     426>>> TestNotRequiredFileForm = form_for_model(NotRequiredFile)
     427>>> filled_not_required_fileform=TestNotRequiredFileForm(no_file_form_data)
     428>>> filled_not_required_fileform.is_valid()
     429True
     430>>> filled_not_required_fileform=TestNotRequiredFileForm(bad_encoding_form_data)
     431>>> print filled_not_required_fileform.errors
     432<ul class="errorlist"><li>file<ul class="errorlist"><li>No file was submitted. Check the encoding type on the form.</li></ul></li></ul>
    380433None
    381434>>> f.clean('')
    382435>>> f.clean('1')
  • tests/regressiontests/forms/tests.py

    === tests/regressiontests/forms/tests.py
    ==================================================================
     
    168168# FileInput Widget ############################################################
    169169
    170170>>> w = FileInput()
    171 >>> w.render('email', '')
    172 u'<input type="file" name="email" />'
    173 >>> w.render('email', None)
    174 u'<input type="file" name="email" />'
    175 >>> w.render('email', 'test@example.com')
    176 u'<input type="file" name="email" value="test@example.com" />'
    177 >>> w.render('email', 'some "quoted" & ampersanded value')
    178 u'<input type="file" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
    179 >>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
    180 u'<input type="file" name="email" value="test@example.com" class="fun" />'
     171>>> w.render('file', '')
     172u'<input type="file" name="file" />'
    181173
    182 You can also pass 'attrs' to the constructor:
    183 >>> w = FileInput(attrs={'class': 'fun'})
    184 >>> w.render('email', '')
    185 u'<input type="file" class="fun" name="email" />'
    186 >>> w.render('email', 'foo@example.com')
    187 u'<input type="file" class="fun" value="foo@example.com" name="email" />'
    188 
    189 >>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
    190 u'<input type="file" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
    191 
    192174# Textarea Widget #############################################################
    193175
    194176>>> w = Textarea()
Back to Top