Django视图中处理ModelForm的FileField及字段赋值方案
问题根源
表单校验不通过是三个错误导致的:
- 你把
user外键字段放在了表单的渲染字段列表里,但前端没有给这个下拉选择框传有效值,提交时该字段为空直接触发必填校验失败 - 视图层赋值用户时写错了对象:你把
request.user赋给了表单对象form,而不是form.save(commit=False)生成的待存储模型实例,就算校验过了用户字段也存不进去 - 原视图没有处理GET请求场景,直接访问上传页时模板拿不到form对象会直接报错
正确实现方案
1. 修改表单定义
既然user、plagiarism_status、serialised_content三个字段都由后端自动赋值,不需要前端传参,直接从表单字段列表中移除即可,既避免校验失败,也能防止恶意用户伪造请求篡改字段值。
class UploadedDocumentsForm(forms.ModelForm): class Meta: model = UploadedDocuments # 只保留需要前端提交的文件字段 fields = ['document'] widgets = { 'document': forms.FileInput( attrs = { 'class': 'drop-zone__input', 'id': 'file', 'accept': '.doc, .docx, .txt', 'required': True, } ) }
2. 修改视图逻辑
添加登录校验避免匿名用户上传报错,区分GET/POST请求逻辑,所有后端自动赋值的字段都要绑定到save(commit=False)返回的模型实例上,不要直接修改form对象。
from django.contrib.auth.decorators import login_required from django.shortcuts import render, redirect @login_required def upload(request): if request.method == "POST": form = UploadedDocumentsForm(request.POST, request.FILES) if form.is_valid(): # 先不写入数据库,拿到内存中的模型实例 doc_instance = form.save(commit=False) # 补全后端自动生成的字段 doc_instance.user = request.user doc_instance.serialised_content = "bala" doc_instance.plagiarism_status = 'Hello' # 所有字段补全后再写入数据库 doc_instance.save() return redirect("results") else: # GET请求初始化空表单 form = UploadedDocumentsForm() # 校验失败或GET请求时,返回带表单的渲染页面 return render(request, "替换为你的模板实际路径.html", {"form": form})
3. 修改模板
移除表单中已经删除的三个字段的渲染代码,可添加表单错误展示块方便调试校验问题。注意文件上传表单必须保留enctype='multipart/form-data'属性,这点你原有代码是正确的,不需要改。
<form action="{% url 'upload' %}" method="POST" enctype='multipart/form-data'> {% csrf_token %} <!-- 表单错误展示 --> {% if form.errors %} <div class="alert alert-danger"> {% for field in form %} {% for error in field.errors %} <p>{{ field.label }}: {{ error }}</p> {% endfor %} {% endfor %} </div> {% endif %} <div class="drop-zone w-50"> <span class="drop-zone__prompt text-center h3"> 拖拽文件到此处 </span> {{ form.document }} </div> <div class="d-flex justify-content-center"> <button type="submit" class="btn btn-white btn-outline-success p-4 px-5 w-lg-25 w-md-50 w-sm-50 text-capitalize my-5"> <i class="fa fa-2x fa-sync px-3"></i> 上传 </button> </div> </form>
额外注意事项
- 所有需要后端自动赋值的字段,都不要放到表单
fields列表里,不要依赖隐藏input传值,隐藏字段可以被用户随意篡改,存在安全风险 - 操作FileField、ImageField类的文件上传字段时,初始化表单必须传入
request.FILES,否则拿不到上传的文件数据 form.save(commit=False)返回的是当前表单对应的模型实例,所有额外字段赋值都要操作这个实例,直接修改form对象本身不会生效
内容的提问来源于stack exchange,提问作者Steve Yonkeu
相关产品推荐
相关产品推荐

