Django图片无法上传求助:两种产品记录保存方式均失效
Django产品图片上传问题排查与解决
我尝试两种方式保存产品记录,但图片上传均失败:
- Django Form方式:调用
form.save()时触发表单验证错误,提示main_img、img1等图片字段为必填项,但移除这些字段后记录能正常保存。 - Model的create方法:记录能成功保存,但图片并未上传到
media文件夹,Django后台仅显示文件名,实际文件不存在。
已确认的正确配置:
- 项目
urls.py中已配置urlpattern + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) - 模型中图片字段已设置
upload_to='product_img' - Django后台管理界面可正常上传图片
相关代码
ModelForm代码
class ProductForm(ModelForm): class Meta: model= ProductDetails fields= ("name","category","subcategory","price","mrp","product_details","main_img","img1","img2","img3") labels={ "name":"Product Name", "product_details":"Description", "category":"Category", "subcategory":"Sub-Category", "price":"Price", "mrp":"MRP", "main_img":"Main Image", "img1":"Image 1", "img2":"Image 2", "img3":"Image 3", } widgets = { 'name':forms.TextInput(attrs={'class':'form-control validate',}), 'product_details':forms.TextInput(attrs={'class':'form-control validate',}), 'category':forms.TextInput(attrs={'class':'custom-select tm-select-accounts',}), 'subcategory':forms.TextInput(attrs={'class':'custom-select tm-select-accounts',}), 'price':forms.TextInput(attrs={'class':'form-control validate',}), 'mrp':forms.TextInput(attrs={'class':'form-control validate',}), 'main_img':forms.FileInput(attrs={'class':'btn btn-primary btn-block mx-auto',}), 'img1':forms.FileInput(attrs={'class':'btn btn-primary btn-block mx-auto',}), 'img2':forms.FileInput(attrs={'class':'btn btn-primary btn-block mx-auto',}), 'img3':forms.FileInput(attrs={'class':'btn btn-primary btn-block mx-auto',}), }
Model代码
# For Product details class ProductDetails(models.Model): name= models.CharField(max_length=100) price= models.FloatField() mrp= models.FloatField() main_img = models.ImageField(upload_to='product_img') img1 = models.ImageField(upload_to='product_img') img2 = models.ImageField(upload_to='product_img') img3 = models.ImageField(upload_to='product_img') category = models.ForeignKey(Category, related_name='produits', on_delete=models.CASCADE) subcategory = models.ForeignKey(SubCategory, related_name='produits', on_delete=models.CASCADE) product_details = RichTextField(blank=True, null=True) trending = models.BooleanField(default=False) def __str__(self): return self.name
方法1(Django Form)的View代码
def add_product(request): if request.method == "POST": form = ProductForm(request.POST or None, request.FILES or None) print(form.errors ) if form.is_valid(): category_id = request.POST.get('category') subcategory_id = request.POST.get('subcategory') category= Category.objects.get(id=int(category_id)) subcategory= SubCategory.objects.get(id=int(subcategory_id)) form.category = category form.subcategory = subcategory form.save() return redirect("/dashboard/products") form = ProductForm() categories=Category.objects.all() subcategories=SubCategory.objects.all() return render(request, "01-add-product.html", {"form":form,"categories":categories,"subcategories":subcategories})
方法2(Model create)的View代码
def add_product(request): if request.method == "POST": name = request.POST['name'] product_details = request.POST['product_details'] category_id = request.POST.get('category') subcategory_id = request.POST.get('subcategory') category= Category.objects.get(id=int(category_id)) subcategory= SubCategory.objects.get(id=int(subcategory_id)) price = request.POST['price'] mrp = request.POST['mrp'] main_img = request.POST['main_img'] img1 = request.POST['img1'] img2 = request.POST['img2'] img3 = request.POST['img3'] product = ProductDetails.objects.create(name=name,product_details=product_details,category=category,subcategory=subcategory,price=price,mrp=mrp,main_img=main_img,img1=img1,img2=img2,img3=img3) product.save()
模板代码
{% extends '01-admin-base.html' %} {% load static %} {% block content %} <div class="container tm-mt-big tm-mb-big"> <div class="row"> <div class="col-xl-9 col-lg-10 col-md-12 col-sm-12 mx-auto"> <div class="tm-bg-primary-dark tm-block tm-block-h-auto"> <div class="row"> <div class="col-12"> <h2 class="tm-block-title d-inline-block">Add Product</h2> </div> </div> <div class="row tm-edit-product-row"> <div class="col-xl-6 col-lg-6 col-md-12"> <form action="" method="post" class="tm-edit-product-form"> {% csrf_token %} {{form.media}} <div class="form-group mb-3"> <label for="name" >Product Name </label> {{form.name}} </div> <div class="form-group mb-3"> <label for="category" >Category</label > <select class="custom-select tm-select-accounts" id="category" name="category" > <option selected>Select category</option> {% for category in categories %} <option value="{{category.id}}">{{category.name}}</option> {% endfor %} </select> </div> <div class="form-group mb-3"> <label for="subcategory" >Sub Category</label > <select class="custom-select tm-select-accounts" id="subcategory" name="subcategory" > <option selected>Select sub-category</option> {% for subcategory in subcategories %} <option value="{{subcategory.id}}">{{subcategory.name}}</option> {% endfor %} </select> </div> <div class="row"> <div class="form-group mb-3 col-xs-12 col-sm-6"> <label for="price" >Price </label> {{form.price}} </div> <div class="form-group mb-3 col-xs-12 col-sm-6"> <label for="mrp" >MRP </label> {{form.mrp}} </div> </div> <div class="form-group mb-3"> <label for="description" >Description</label > {{form.product_details}} </div> </div> <div class="col-xl-6 col-lg-6 col-md-12 mx-auto mb-4"> <div class="custom-file mt-3 mb-3"> <label>Main Image</label> {{form.main_img}} </div> <br><br> <label>Images</label> <div class="custom-file mt-3 mb-3"> {{form.img1}} </div> <div class="custom-file mt-3 mb-3"> {{form.img2}} </div> <div class="custom-file mt-3 mb-3"> {{form.img3}} </div> </div> <div class="col-12"> <button type="submit" class="btn btn-primary btn-block text-uppercase">Add Product Now</button> </div> </form> </div> </div> </div> </div> </div> {% endblock content %}
问题修复方案
针对方法1(Django Form方式)的修复
- 添加表单文件上传属性:上传文件的表单必须声明
enctype="multipart/form-data",否则文件数据不会被提交到后端。修改模板中的form标签:
<form action="" method="post" class="tm-edit-product-form" enctype="multipart/form-data">
- 修正外键字段处理逻辑:直接给
form.category赋值无效,表单会自动处理模板中select提交的外键ID,无需手动查询设置。修改View代码:
def add_product(request): if request.method == "POST": form = ProductForm(request.POST, request.FILES) print(form.errors ) if form.is_valid(): form.save() return redirect("/dashboard/products") form = ProductForm() categories=Category.objects.all() subcategories=SubCategory.objects.all() return render(request, "01-add-product.html", {"form":form,"categories":categories,"subcategories":subcategories})
- 修正外键字段的Widget:ModelForm中外键字段应使用
Select而非TextInput,匹配模板中的下拉框类型:
widgets = { # ... 其他widget保持不变 'category':forms.Select(attrs={'class':'custom-select tm-select-accounts',}), 'subcategory':forms.Select(attrs={'class':'custom-select tm-select-accounts',}), # ... }
针对方法2(Model create方式)的修复
同样添加表单
enctype属性:和方法1一致,模板form标签必须加enctype="multipart/form-data"。从
request.FILES获取文件:文件数据不能从request.POST获取,必须从request.FILES中读取文件对象:
def add_product(request): if request.method == "POST": name = request.POST['name'] product_details = request.POST['product_details'] category_id = request.POST.get('category') subcategory_id = request.POST.get('subcategory') category= Category.objects.get(id=int(category_id)) subcategory= SubCategory.objects.get(id=int(subcategory_id)) price = request.POST['price'] mrp = request.POST['mrp'] # 从request.FILES获取文件对象 main_img = request.FILES.get('main_img') img1 = request.FILES.get('img1') img2 = request.FILES.get('img2') img3 = request.FILES.get('img3') product = ProductDetails.objects.create( name=name, product_details=product_details, category=category, subcategory=subcategory, price=price, mrp=mrp, main_img=main_img, img1=img1, img2=img2, img3=img3 ) return redirect("/dashboard/products")
- 可选:允许图片字段为空:如果不需要强制上传所有图片,可在Model中设置字段允许为空:
main_img = models.ImageField(upload_to='product_img', blank=True, null=True) img1 = models.ImageField(upload_to='product_img', blank=True, null=True) img2 = models.ImageField(upload_to='product_img', blank=True, null=True) img3 = models.ImageField(upload_to='product_img', blank=True, null=True)
执行数据库迁移命令:
python manage.py makemigrations python manage.py migrate
内容的提问来源于stack exchange,提问作者Chetan Rathod
相关产品推荐
相关产品推荐

