django文件上传 - 更新编辑模板时删除的图像

时间:2015-02-20 08:57:42

标签: python django image file-upload django-file-upload

我使用django允许用户上传图片以及文字来描述图片标题。

用户应该能够使用编辑模板编辑图像标题并更改图像文件。这确实有效 - 在某种程度上。

我可以在编辑模板中显示图像文件和图像标题进行编辑。我的问题是当我没有在文件上传输入中包含图像文件时(用户只想更改图像标题而不是图像文件),我的代码确实更改了图像标题但删除了图像文件从数据库(不从文件系统中删除物理图像文件)。

我可以成功更新图像细节,但用户必须在图像标题的文件上传输入字段中包含新图像文件或相同图像文件,才能成功更新图像细节。

似乎用户必须在文件上传输入字段中包含图像文件。我试图使我的代码像django管理员一样工作,用户每次用户更改图像标题时都不必包含图像文件。

每次用户想要更改图片标题时,如何让用户更新图片详细信息而无需重新上传图片文件?

这是我的models.py代码:

编辑#2 - 添加了FillableModelWithLanguageVersion& LanguageVersion

# START: ATTACHMENT DETAILS MODEL.
def _get_document_upload_location(instance, filename):
    """
    Using a function instead of a lambda to make migrations happy. DO NOT remove or rename this function in the future, as it will break migrations.
    @param instance: model instance that owns the FileField we're generating the upload filename for.
    @param filename: Original file name assigned by django.
    """
    return 'attachments/%d/%s' % (instance.user.id, uuid.uuid4())

class AttachmentDetails(models.Model, FillableModelWithLanguageVersion):
    user = models.ForeignKey(User)
    language_version = models.ForeignKey('LanguageVersion')
    attachment_document = models.FileField(upload_to=_get_document_upload_location, null=True, blank=True)
    attachment_title = models.CharField(null=False, blank=False, max_length=250)
    attachment_timestamp_added = models.DateTimeField(auto_now_add=True, auto_now=False)
    attachment_timestamp_updated = models.DateTimeField(auto_now=True, auto_now_add=False)

    def __unicode__(self):
        return unicode(self.user)

    class Meta:
        verbose_name = ('Attachment Detail')
        verbose_name_plural = ('Attachment Details')

    @staticmethod
    def view_link():
        return reverse(settings.MENU_DETAIL_LINK_ATTACHMENT_DETAILS)

    @property
    def language_name(self):
        return LANGUAGES[self.language_version.language_code].name

    @property
    def language_name_english(self):
        return LANGUAGES[self.language_version.language_code].name_english

    @property
    def language_name_native(self):
        return LANGUAGES[self.language_version.language_code].name_native


# delete file when AttachmentDetails model is deleted


@receiver(post_delete,sender=AttachmentDetails,dispatch_uid='delete_attachment_details')
def delete_attachment_details(sender, **kwargs):
    attachment_details = kwargs['instance']
    attachment_details.attachment_document.delete(save=False)
# FINISH: ATTACHMENT DETAILS MODEL.

class FillableModelWithLanguageVersion(object):
    def fill(self, fields):
        self.language_version = LanguageVersion.objects.get(user=self.user, language_code=fields['language_code'])
        for field, value in fields.iteritems():
            if field == 'language_code':
                continue
            setattr(self, field, value)

class LanguageVersion(models.Model):
    """Language version selection for a user"""
    user = models.ForeignKey(User)
    language_code = models.CharField(max_length=32)
    language_code_disabled = models.BooleanField(default=False)
    language_version_timestamp_added = models.DateTimeField(auto_now_add=True, auto_now=False)
    language_version_timestamp_updated = models.DateTimeField(auto_now=True, auto_now_add=False)  # the date the language version is updated when the user changes their subscription type.

    def __unicode__(self):
        return unicode(self.language_code)

    class Meta:
        unique_together = ('user', 'language_code')
        verbose_name = ('Language Versions')
        verbose_name_plural = ('Language Versions')

这是我的views.py代码:

def attachment_details_edit(request, attachment_details_id):
    try:
        attachment_details = AttachmentDetails.objects.get(pk=attachment_details_id, user=request.user)
    except AttachmentDetails.DoesNotExist:
        return redirect(settings.MENU_DETAIL_LINK_ATTACHMENT_DETAILS)
    language_versions = LanguageVersion.objects.filter(user=request.user).select_related('language_version')
    available_languages = get_available_language_details(language_versions, request.user.userprofile.language_preference)
    attachment_details_num = request.user.attachmentdetails_set.count()
    language_code = attachment_details.language_version.language_code
    language_code_disabled = attachment_details.language_version.language_code_disabled
    language_preference = request.user.userprofile.language_preference
    if language_code_disabled:
        return redirect(settings.MENU_DETAIL_LINK_ATTACHMENT_DETAILS)
    if request.method == 'GET':
        language_code = attachment_details.language_version.language_code
        form = AttachmentDetailsForm(
                available_languages,
                language_preference=request.user.userprofile.language_preference,
                file_required=True,
                initial=dict(
                    model_to_dict(attachment_details),
                    language_code=language_code
                )
        )
    elif request.method == 'POST':
        form = AttachmentDetailsForm(
            available_languages,
            language_preference,
            False,  # file_required
            request.POST,
            request.FILES
        )
        if form.is_valid():
            cd = form.cleaned_data
            if cd['attachment_document'] is not None:
                #  delete the existing uploaded attachment when user updates the existing attachment with a replacement attachment.
                print 'removing previously uploaded file'
                attachment_details.attachment_document.delete(save=False)
            attachment_details.fill(cd)
            attachment_details.save()
            messages.success(request, _('successfully updated.'))
            return redirect(settings.MENU_DETAIL_LINK_ATTACHMENT_DETAILS)

我有一篇相关的帖子here

修改

这是我的forms.py代码:

class AttachmentDetailsForm(forms.ModelForm):

    required_css_class = 'required'

    def __init__(self, available_languages, language_preference, file_required, *args, **kwargs):
        """
        available_languages should be a valid choices list
        """
        super(AttachmentDetailsForm, self).__init__(*args, **kwargs)
        self.fields['language_code'] = forms.ChoiceField(choices=available_languages, initial=language_preference, label=_('Language'),)
        self.fields['attachment_document'] = forms.FileField(label=_('Attachment'), required=file_required)

    class Meta:
        model = AttachmentDetails

        fields = (
            'attachment_title',
        )

        labels = {
            'attachment_title': _('Attachment Title'),
        }

    def clean_attachment_document(self):

        if self.cleaned_data['attachment_document'] is not None:

            file_name = self.cleaned_data['attachment_document'].name.lower()
            extension = file_name.split('.')[-1]

            if extension not in settings.ALLOWED_ATTACHMENT_EXTENSIONS:
                raise forms.ValidationError(_("Only file types .bmp, .gif, .jpg, .jpeg are permitted."))

            # use the following if condition when the file size of the attachment is to be measured in MB - MB calculation.
            #if self.cleaned_data['attachment_document'].size > settings.MAX_ATTACHMENT_FILE_SIZE_MB * 1024 * 1024:
            #    raise forms.ValidationError('Maximum permitted attachment size is: %d MB.' % settings.MAX_ATTACHMENT_FILE_SIZE_MB)

            # use the following if condition when the file size of the attachment is to be measured in kB - kB calculation.
            if self.cleaned_data['attachment_document'].size > settings.MAX_ATTACHMENT_FILE_SIZE_KB * 1024:
                raise forms.ValidationError('Maximum permitted attachment size is: %d kB.' % settings.MAX_ATTACHMENT_FILE_SIZE_KB)

        return self.cleaned_data['attachment_document']

2 个答案:

答案 0 :(得分:3)

您的问题来自您模型的fill方法。它总是设置attachment_document。为了防止这种情况 - 试试这个:

    if form.is_valid():
        cd = form.cleaned_data
        attachment_document = cd.pop('attachment_document')
        if attachment_document:
            #  delete the existing uploaded attachment when user updates the existing attachment with a replacement attachment.
            print 'removing previously uploaded file'
            attachment_details.attachment_document.delete(save=False)
            attachment_details.attachment_document = attachment_document
        attachment_details.fill(cd)
        attachment_details.save()
        messages.success(request, _('successfully updated.'))
        return redirect(settings.MENU_DETAIL_LINK_ATTACHMENT_DETAILS)

答案 1 :(得分:0)

没试过,但看起来你在发布编辑过的表格时没有设置实例参数。您可以像这样尝试,然后只需在表单上调用save方法:

elif request.method == 'POST':
    form = AttachmentDetailsForm(
        available_languages,
        language_preference,
        False,  # file_required
        request.POST,
        request.FILES,
        instance=attachment_details
    )
    if form.is_valid():
        form.save()
        messages.success(request, _('successfully updated.'))
        return redirect(settings.MENU_DETAIL_LINK_ATTACHMENT_DETAILS)