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

Django图片无法上传求助:两种产品记录保存方式均失效

Django产品图片上传问题排查与解决

我尝试两种方式保存产品记录,但图片上传均失败:

  1. Django Form方式:调用form.save()时触发表单验证错误,提示main_img、img1等图片字段为必填项,但移除这些字段后记录能正常保存。
  2. 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方式)的修复

  1. 添加表单文件上传属性:上传文件的表单必须声明enctype="multipart/form-data",否则文件数据不会被提交到后端。修改模板中的form标签:
<form action="" method="post" class="tm-edit-product-form" enctype="multipart/form-data">
  1. 修正外键字段处理逻辑:直接给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})
  1. 修正外键字段的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方式)的修复

  1. 同样添加表单enctype属性:和方法1一致,模板form标签必须加enctype="multipart/form-data"。

  2. 从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")
  1. 可选:允许图片字段为空:如果不需要强制上传所有图片,可在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 22:20:41