You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django表单无法更新图片,添加request.FILES后表单空白求助

问题描述

我用Crispy Forms构建了一个Django表单,能正常创建新对象、更新文本内容,但无法更新图片。尝试在表单实例化时添加request.FILES后,表单不再加载待更新的对象信息,变成空白表单。以下是相关代码:

Model代码

class Bottle(models.Model):
    class displaySorting(models.IntegerChoices):
        Lighthouse = 1
        Regular = 2

    name = models.CharField(max_length=255)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    sorting = models.IntegerField(choices = displaySorting.choices,verbose_name='Sort order')
    brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
    bottle_size = models.IntegerField(validators=[MaxValueValidator(9999)])
    info = HTMLField()
    tasting_notes = HTMLField()
    abv = models.DecimalField(max_digits=3, decimal_places=1, verbose_name='Alcohol %')
    image = ResizedImageField(size=[1000,1000], upload_to=image_upload_handler)
    thumbnail = models.ImageField(upload_to='thumbnails')
    shop_link = models.URLField()
    consumer_shop_link = models.URLField()
    website_link = models.URLField()

    def save(self, *args, **kwargs):
        self.slug = slugify(f"{self.brand}-{self.name}")
        output_size = (300, 169)
        output_thumb = BytesIO()

        img = Image.open(self.image)
        img_name = self.image.name.split('.')[0]

        if img.height > 300 or img.width > 300:
            img.thumbnail(output_size)
            img.save(output_thumb,format='JPEG',quality=90)

        self.thumbnail = InMemoryUploadedFile(output_thumb, 'ImageField', f"{self.brand}-{img_name}_thumb.jpg", 'image/jpeg', sys.getsizeof(output_thumb), None)

        super(Bottle, self).save()

View代码

@login_required
def bottle_update_view(request, id=None):
    object = get_object_or_404(Bottle, id=id)
    form = BottleForm(request.POST or None, request.FILES, instance=object)

    context = {
        'form':form,
        'object':object
    }

    if form.is_valid():
        form.save()
        context['message'] = 'Data saved'

    return render(request,'Academy/bottle_crud.html',context)

Template代码

{% block content %}

<div class='container'> 
    <div class='row form'>
        <h1 class='text-center my-5'>Add & Update bottle</h1>
       {% if message %}
            <div> 
                <div><h2>{{message}}</h2></div>
                <div><a href="{{object.get_bottle_link}}"><h2 class='link'>Check bottle</h2></a></div>
            </div>
        
       {% endif %}

       <form enctype='multipart/form-data' method='POST'>
        {% crispy form %}
        </form>
    </div>
</div>

{% endblock content %}
问题排查与修复

1. 表单实例化参数错误

View中表单实例化时,request.FILES的传参逻辑有误。当请求为GET时,request.POST是None,但此时传入request.FILES会导致表单无法正确绑定实例数据——因为GET请求没有文件上传内容,进而导致表单空白。

修复后的View代码:

@login_required
def bottle_update_view(request, id=None):
    object = get_object_or_404(Bottle, id=id)
    # 根据请求方法区分传参:POST请求才传入文件数据,GET请求仅绑定实例
    if request.method == 'POST':
        form = BottleForm(request.POST, request.FILES, instance=object)
    else:
        form = BottleForm(instance=object)

    context = {
        'form': form,
        'object': object
    }

    if form.is_valid():
        form.save()
        context['message'] = 'Data saved'

    return render(request, 'Academy/bottle_crud.html', context)

2. 模型save方法的逻辑缺陷

原save方法每次执行都会强制处理self.image,但更新时如果没有上传新图片,直接调用Image.open(self.image)可能引发错误,还会无意义地覆盖原有缩略图。

优化后的Model save方法:

import sys
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
from PIL import Image
from django.utils.text import slugify

class Bottle(models.Model):
    # 原有字段定义保持不变...

    def save(self, *args, **kwargs):
        self.slug = slugify(f"{self.brand}-{self.name}")
        # 仅当image字段有新上传的文件时,才生成新的缩略图
        if self.image and hasattr(self.image, 'file'):
            output_size = (300, 169)
            output_thumb = BytesIO()

            img = Image.open(self.image)
            img_name = self.image.name.split('.')[0]

            if img.height > 300 or img.width > 300:
                img.thumbnail(output_size)
                img.save(output_thumb, format='JPEG', quality=90)

            self.thumbnail = InMemoryUploadedFile(
                output_thumb, 
                'ImageField', 
                f"{self.brand}-{img_name}_thumb.jpg", 
                'image/jpeg', 
                sys.getsizeof(output_thumb), 
                None
            )
        # 必须传递args和kwargs给父类save方法
        super(Bottle, self).save(*args, **kwargs)

3. 表单字段配置检查

确保BottleForm正确包含image字段,且未强制要求必填(除非业务需要每次更新都上传图片):

from django import forms
from .models import Bottle

class BottleForm(forms.ModelForm):
    class Meta:
        model = Bottle
        fields = '__all__'  # 或明确列出包含image的字段列表
        # 可选:设置图片字段为非必填
        widgets = {
            'image': forms.ClearableFileInput(attrs={'required': False}),
        }

内容的提问来源于stack exchange,提问作者Thomas Doll-Datema

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 02:54:55