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
相关产品推荐
相关产品推荐

