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

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

Patch for current trunk, changes: clean_data -> cleaned_data and new formfield-method for model-fields

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

    ==== Patch <django-newforms-file-imagefield> level 3
    Source: f4626db1-382e-0410-8594-a82d82b39f71:/newforms-file-imagefield/trunk:5184 [local]
    Target: bcc190cf-cafb-0310-a4f2-bffc1f526a37:/django/trunk:5245 [mirrored]
            (http://code.djangoproject.com/svn/django)
    Log:
     r4876@damasonium:  ddanier | 2007-04-16 14:26:42 +0200
      * FileField and ImageField for newforms not using additional <input>'s
      * FileInput is now the default-widget for FileField
     r5183@damasonium:  ddanier | 2007-05-15 17:25:46 +0200
      * Updated models.FileField and models.ImageField to use new formfield()-base in Field-class
     r5184@damasonium:  ddanier | 2007-05-15 17:33:03 +0200
      * clean_data -> cleaned_data
    
    === django/db/models/fields/__init__.py
    ==================================================================
     
    665665            else:
    666666                func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"], save)
    667667
     668    def formfield_save_file(self, field_name, new_data, new_object, save=True):
     669        if new_data[field_name]:
     670            func = getattr(new_object, 'save_%s_file' % self.name)
     671            func(new_data[field_name]["filename"], new_data[field_name]["content"], save)
     672
    668673    def get_directory_name(self):
    669674        return os.path.normpath(datetime.datetime.now().strftime(self.upload_to))
    670675
     
    673678        f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))
    674679        return os.path.normpath(f)
    675680
     681    def formfield(self, **kwargs):
     682        defaults = {'form_class': forms.FileField}
     683        defaults.update(kwargs)
     684        return super(FileField, self).formfield(**defaults)
     685
    676686class FilePathField(Field):
    677687    def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
    678688        self.path, self.match, self.recursive = path, match, recursive
     
    719729                setattr(new_object, self.height_field, getattr(original_object, self.height_field))
    720730            new_object.save()
    721731
     732    def formfield(self, **kwargs):
     733        defaults = {'form_class': forms.ImageField}
     734        defaults.update(kwargs)
     735        return super(ImageField, self).formfield(**defaults)
     736
    722737class IntegerField(Field):
    723738    empty_strings_allowed = False
    724739    def get_manipulator_field_objs(self):
  • django/newforms/models.py

    === django/newforms/models.py
    ==================================================================
     
    2828    for f in opts.fields:
    2929        if not f.editable or isinstance(f, models.AutoField) or not f.name in cleaned_data:
    3030            continue
     31        if isinstance(f, models.FileField):
     32            continue
    3133        if fields and f.name not in fields:
    3234            continue
    3335        setattr(instance, f.name, cleaned_data[f.name])
     36
     37    # FileField may need more info to save to specific paths
     38    for f in opts.fields:
     39        if isinstance(f, models.FileField) and cleaned_data.has_key(f.name):
     40            f.formfield_save_file(f.name, cleaned_data, instance, save=False)
     41
    3442    if commit:
    3543        instance.save()
    3644        for f in opts.many_to_many:
     
    9199    takes a database Field instance, plus **kwargs, and returns a form Field
    92100    instance with the given kwargs (i.e. 'initial').
    93101    """
     102    from django.db import models
    94103    model = instance.__class__
    95104    opts = model._meta
    96105    field_list = []
     
    102111        current_value = f.value_from_object(instance)
    103112        formfield = formfield_callback(f, initial=current_value)
    104113        if formfield:
     114            # FileFields are only required once, because we cannot print the
     115            # existing data into the <form>
     116            if isinstance(f, models.FileField) and current_value:
     117                formfield.required = False
    105118            field_list.append((f.name, formfield))
    106119    base_fields = SortedDictFromList(field_list)
    107120    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
    ==================================================================
     
    6868    def __str__(self):
    6969        return self.phone
    7070
     71class NotRequiredFile(models.Model):
     72    description = models.CharField(maxlength=50)
     73    file = models.FileField(upload_to='files', blank=True)
     74
     75    def __str__(self):
     76        return self.description
     77
     78class RequiredFile(models.Model):
     79    description = models.CharField(maxlength=50)
     80    file = models.FileField(upload_to='files')
     81
     82    def __str__(self):
     83        return self.description
     84
    7185__test__ = {'API_TESTS': """
    7286>>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField
    7387>>> import datetime
     
    442456
    443457>>> f = ModelChoiceField(Category.objects.filter(pk=1), required=False)
    444458>>> print f.clean('')
     459
     460Test for filefield
     461
     462>>> form_data = {'description': 'FileField test', 'file': {'content': 'FileField test', 'filename': 'filetest.txt'} }
     463>>> no_file_form_data = {'description': 'FileField test'}
     464>>> bad_encoding_form_data = {'description': 'FileField test', 'file': 'wrong encoding'}
     465>>> TestRequiredFileForm = form_for_model(RequiredFile)
     466>>> empty_required_fileform = TestRequiredFileForm(auto_id=False)
     467>>> print empty_required_fileform.as_ul()
     468<li>Description: <input type="text" name="description" maxlength="50" /></li>
     469<li>File: <input type="file" name="file" /></li>
     470
     471Test with data
     472>>> filled_required_fileform= TestRequiredFileForm(form_data, auto_id=False)
     473>>> filled_required_fileform.is_valid()
     474True
     475
     476>>> required_file_instance = filled_required_fileform.save()
     477>>> print required_file_instance.file
     478files/filetest.txt
     479>>> TestInstanceRequiredFileForm = form_for_instance(required_file_instance)
     480>>> instance_required_fileform = TestInstanceRequiredFileForm(auto_id=False)
     481>>> instance_required_fileform.as_ul()
     482u'<li>Description: <input type="text" name="description" value="FileField test" maxlength="50" /></li>\\n<li>File: <input type="file" name="file" /></li>'
     483>>> required_file_instance.delete()
     484
     485Bad data test
     486>>> filled_required_fileform=TestRequiredFileForm(no_file_form_data)
     487>>> filled_required_fileform.is_valid()
     488False
     489>>> print filled_required_fileform.errors
     490<ul class="errorlist"><li>file<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
     491>>> TestNotRequiredFileForm = form_for_model(NotRequiredFile)
     492>>> filled_not_required_fileform=TestNotRequiredFileForm(no_file_form_data)
     493>>> filled_not_required_fileform.is_valid()
     494True
     495>>> filled_not_required_fileform=TestNotRequiredFileForm(bad_encoding_form_data)
     496>>> print filled_not_required_fileform.errors
     497<ul class="errorlist"><li>file<ul class="errorlist"><li>No file was submitted. Check the encoding type on the form.</li></ul></li></ul>
    445498None
    446499>>> f.clean('')
    447500>>> f.clean('1')
  • tests/regressiontests/forms/tests.py

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