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

Python django文件上载-更新编辑模板时删除图像,python,django,image,file-upload,django-file-upload,Python,Django,Image,File Upload,Django File Upload,我使用django允许用户上传图像以及描述图像标题的文本 用户应该能够使用编辑模板编辑图像标题和更改图像文件。在某种程度上,这确实有效 我可以在编辑模板中显示图像文件和图像标题进行编辑。我遇到的问题是,当我没有在文件上载输入中包含图像文件时(用户只想更改图像标题,而不是图像文件),我的代码确实更改了图像标题,但图像文件已从数据库中删除(物理图像文件未从文件系统中删除) 我可以成功更新图像详细信息,但用户必须在文件上载输入字段中包含新的图像文件或相同的图像文件,才能成功更新图像标题和图像详细信息

我使用django允许用户上传图像以及描述图像标题的文本

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

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

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

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

如何允许用户更新图像详细信息,而无需在每次用户想要更改图像标题时重新上载图像文件?

以下是我的models.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)
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-添加了带有语言版本和语言版本的可填充模型

# 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')
以下是my 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)
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']
我有一个相关的职位

编辑

这是我的forms.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)
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']

未尝试此操作,但在发布已编辑表单时,似乎未设置实例参数。您可以这样尝试,然后只需在表单上调用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)

未尝试此操作,但在发布已编辑表单时,似乎未设置实例参数。您可以这样尝试,然后只需在表单上调用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)

您的问题来自模型的
fill
方法。它总是设置
附件\u文档
。为了防止这种情况,请尝试以下方法:

    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)

您的问题来自模型的
fill
方法。它总是设置
附件\u文档
。为了防止这种情况,请尝试以下方法:

    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)

查看您的代码,我认为您的问题来自于
附件\u详细信息。fill(cd)
您能提供该方法的源代码吗?Todor,我在帖子底部添加了我的forms.py代码作为编辑-这有帮助吗?我想看看
fill
方法的作用。我相信它在你的
AttachmentDetails
model类中。Todor,我已经更新了帖子以包含所有models.py AttachmentDetails类代码。这就是你所需要的吗?几乎……)似乎
fill
方法是从
fillebleModelWithLanguageVersion
继承的?请也提供该类。查看您的代码我认为您的问题来自
附件\u详细信息。fill(cd)
您可以提供该方法的源代码吗?Todor,我在帖子底部添加了我的forms.py代码作为编辑-这有帮助吗?我想看看
fill
方法的作用。我相信它在你的
AttachmentDetails
model类中。Todor,我已经更新了帖子以包含所有models.py AttachmentDetails类代码。这就是你所需要的吗?几乎……)似乎
fill
方法是从
fillebleModelWithLanguageVersion
继承的?请提供该类。necj92,我认为你在正确的道路上。我在文章底部添加了forms.py代码作为编辑,因为我不确定如何将实例添加到forms.py代码调用中。您不必更改forms.py文件。初始化AttachmentDetails表单时只需添加实例参数。nejc92,我按照您的建议添加了
instance=attachment\u details
,但出现了相同的问题。但您是否仍在使用attachment\u details.fill(cd)和attachment\u details.save(),然后再使用form.save()?您不必这样做,因为如果在初始化form.necj92时设置了instance,那么form.save()将更新数据库中相应的行。我认为您的路径是正确的。我在文章底部添加了forms.py代码作为编辑,因为我不确定如何将实例添加到forms.py代码调用中。您不必更改forms.py文件。初始化AttachmentDetails表单时只需添加实例参数。nejc92,我按照您的建议添加了
instance=attachment\u details
,但出现了相同的问题。但您是否仍在使用attachment\u details.fill(cd)和attachment\u details.save(),然后再使用form.save()?您不必这样做,因为如果初始化表单时设置了实例,则form.save()将更新数据库中相应的行。