Django实现多文件上传并支持表单提交前删除已选文件咨询
问题根因
你遇到的问题本质是浏览器原生<input type="file" multiple>的FileList对象为只读属性,你在JS中维护的filesToUpload数组和DOM显示列表的修改,都不会同步到input自带的文件列表中。表单默认提交时,浏览器会把input中所有选中过的文件都发送到后端,并非Django的特性导致。
解决方案(最小改动版)
不需要重构现有提交逻辑,只需要同步前端有效文件列表到后端做过滤即可,分三步修改:
1. 新增HTML隐藏域
在你的表单内部添加隐藏域,用于存储最终有效文件的标识:
<input type="hidden" name="valid_file_keys" id="valid_file_keys" value="">
2. 修改JS代码同步有效文件标识
先新增一个更新隐藏域的公共方法,每次添加、删除文件时调用,为了避免同名文件误判,我们用「文件名+文件大小」作为唯一标识:
// 新增方法:更新有效文件标识列表 function updateValidFileKeys() { const keys = filesToUpload.map(f => `${f.name}|${f.size}`).join(','); $('#valid_file_keys').val(keys); }
然后分别在两个逻辑点调用这个方法:
- 文件上传change事件中,append完fileList列表后调用
updateValidFileKeys() - 删除文件的点击事件中,splice完
filesToUpload数组、删除对应DOM元素后调用updateValidFileKeys()
3. 后端视图过滤有效文件
修改你的CreateView post方法中文件处理的逻辑,用前端传的有效标识过滤request.FILES的结果:
def post(self, request, *args, **kwargs): if "cancel" in request.POST: return HttpResponseRedirect(reverse('Procedures:procedure_main_menu')) else: self.object = None user = request.user form_class = self.get_form_class() form = self.get_form(form_class) file_form = NewProcedureFilesForm(request.POST, request.FILES) # 改动开始 all_uploaded_files = request.FILES.getlist('files1') valid_file_keys = request.POST.get('valid_file_keys', '').split(',') valid_file_keys = [k for k in valid_file_keys if k.strip()] # 过滤出用户最终留存的文件 valid_files = [] for f in all_uploaded_files: current_key = f"{f.name}|{f.size}" if current_key in valid_file_keys: valid_files.append(f) # 改动结束 if form.is_valid() and file_form.is_valid(): procedure_instance = form.save(commit=False) procedure_instance.user = user procedure_instance.save() # 用过滤后的valid_files保存 for f in valid_files: procedure_file_instance = NewProcedureFiles(attachments=f, new_procedure=procedure_instance) procedure_file_instance.save() return self.form_valid(form) else: form_class = self.get_form_class() form = self.get_form(form_class) file_form = NewProcedureFilesForm() return self.form_invalid(form)
可选优化:AJAX提交方案
如果不想传输多余的文件减少带宽消耗,可以拦截表单默认提交行为,手动构造FormData只传有效文件:
// 给你的表单加个id比如id="upload_form" $('#upload_form').on('submit', function(e) { e.preventDefault(); const formData = new FormData(this); // 先删除默认带的files1所有文件 formData.delete('files1'); // 把有效文件重新加进去 filesToUpload.forEach(f => { formData.append('files1', f); }); // 提交请求 $.ajax({ url: this.action, type: 'POST', data: formData, processData: false, contentType: false, success: function(res) { // 处理提交成功逻辑,比如跳转 window.location.href = res.redirect_url; }, error: function() { // 处理错误逻辑 } }) })
用这个方案的话后端不需要做额外过滤,直接用request.FILES.getlist('files1')拿到的就是有效文件。
内容的提问来源于stack exchange,提问作者Steve Smith
相关产品推荐
相关产品推荐

