Ticket #2534: filefield_core_fixes.diff

File filefield_core_fixes.diff, 6.6 KB (added by anonymous, 9 years ago)

Patch to allow FileFields to be used as core fields

  • django/db/models/manipulators.py

     
    167167                                pass
    168168
    169169                    for f in related.opts.fields:
    170                         if f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) in (None, ''):
     170                        if f.core and f.get_manipulator_new_data(rel_new_data, rel=True) in (None, ''):
    171171                            all_cores_given = False
    172                         elif f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) not in (None, ''):
     172                        elif f.core and f.get_manipulator_new_data(rel_new_data, rel=True) not in (None, ''):
    173173                            all_cores_blank = False
    174174                        # If this field isn't editable, give it the same value it had
    175175                        # previously, according to the given ID. If the ID wasn't
  • django/db/models/fields/__init__.py

     
    532532        validators.isValidEmail(field_data, all_data)
    533533
    534534class FileField(Field):
    535     def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
     535    def __init__(self, verbose_name=None, name=None, upload_to='', can_delete=False, **kwargs):
    536536        self.upload_to = upload_to
     537        self.can_delete = can_delete
    537538        Field.__init__(self, verbose_name, name, **kwargs)
     539        assert (not self.can_delete) or self.null, "A FileField must have null=True if can_delete=True"
    538540
    539541    def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
    540542        field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
     
    572574        field_list[1].validator_list.append(isWithinMediaRoot)
    573575        return field_list
    574576
     577    def get_manipulator_new_data(self, new_data, rel=False):
     578        # Return the value which the field will take, if it is saved.
     579        # This will be either the pathname of the uploaded file, or the
     580        # pathname of the existing file, if no file was uploaded.
     581        if rel:
     582            extract = lambda field: (new_data.get("%s%s" % (self.name, field), [None])[0])
     583        else:
     584            extract = lambda field: (new_data.get("%s%s" % (self.name, field), None))
     585        if extract('_delete'):
     586            return None
     587        try:
     588            file = extract('_file')
     589            return self.get_filename(file['filename'])
     590        except (IndexError, KeyError, TypeError):
     591            pass
     592        return extract('')
     593
    575594    def contribute_to_class(self, cls, name):
    576595        super(FileField, self).contribute_to_class(cls, name)
    577596        setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self))
     
    590609                os.remove(file_name)
    591610
    592611    def get_manipulator_field_objs(self):
     612        if self.can_delete:
     613            return [forms.FileUploadField, forms.HiddenField, forms.LabeledCheckbox]
    593614        return [forms.FileUploadField, forms.HiddenField]
    594615
    595616    def get_manipulator_field_names(self, name_prefix):
     617        if self.can_delete:
     618            return [name_prefix + self.name + '_file', name_prefix + self.name, name_prefix + self.name + '_delete']
    596619        return [name_prefix + self.name + '_file', name_prefix + self.name]
    597620
    598621    def save_file(self, new_data, new_object, original_object, change, rel):
    599         upload_field_name = self.get_manipulator_field_names('')[0]
    600         if new_data.get(upload_field_name, False):
     622        field_names = self.get_manipulator_field_names('')
     623        upload_field_name = field_names[0]
     624        # Delete only if delete checkbox is present and checked
     625        if self.can_delete and ((rel and new_data.get(field_names[2], [False])[0]) or
     626                                (not rel and new_data.get(field_names[2], False))):
     627            file_name = getattr(original_object, 'get_%s_filename' % self.name)()
     628            # If file exists, delete it
     629            if file_name and os.path.exists(file_name):
     630                os.remove(file_name)
     631            setattr(new_object, self.name, None)
     632            new_object.save()
     633        elif new_data.get(upload_field_name, False):
    601634            func = getattr(new_object, 'save_%s_file' % self.name)
    602635            if rel:
    603636                func(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"])
     
    635668        FileField.__init__(self, verbose_name, name, **kwargs)
    636669
    637670    def get_manipulator_field_objs(self):
     671        if self.can_delete:
     672            return [forms.ImageUploadField, forms.HiddenField, forms.LabeledCheckbox]
    638673        return [forms.ImageUploadField, forms.HiddenField]
    639674
    640675    def contribute_to_class(self, cls, name):
  • django/forms/__init__.py

     
    993993            v(field_data, all_data)
    994994        except validators.ValidationError, e:
    995995            raise validators.CriticalValidationError, e.messages
     996
     997class LabeledCheckbox(CheckboxField):
     998    """
     999    A checkbox for which is_required is allowed to be set to False, and
     1000    which optionally displays a text label before its form control.
     1001    """
     1002    def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False, label=None):
     1003        self.label = label
     1004        # This default is to support using this checkbox as a delete field in
     1005        # FileField objects. It is only required because there is no easy way
     1006        # to send unique constructor parameters to multiple manipulator field
     1007        # objects.
     1008        if self.label == None:
     1009            self.label = gettext_lazy("Delete")
     1010        super(LabeledCheckbox, self).__init__(field_name, checked_by_default, validator_list)
     1011
     1012    def render(self, data):
     1013        checked_html = ''
     1014        if data or (data is '' and self.checked_by_default):
     1015            checked_html = ' checked="checked"'
     1016        label_html = ''
     1017        if self.label:
     1018            label_html = " %s " % self.label
     1019        return '%s<input type="checkbox" id="%s" class="v%s" name="%s"%s value="on" />' % \
     1020            (label_html, self.get_id(), self.__class__.__name__,
     1021            self.field_name, checked_html)
     1022
Back to Top