如何在Django CreateView中集成Ajax实现带文件上传的异步表单提交
问题原因排查
你遇到的报错和无响应问题核心是3个错误导致的:
- 前端使用
serialize()提交表单,该方法无法序列化文件类型的表单字段,且带文件上传的表单不能用普通序列化方式提交 - 自定义的
ajax_posting视图中,CreateProcedureForm参数传入顺序错误,导致表单无法正确接收POST数据,所以即使你填了内容也会报必填字段缺失 - 自定义视图完全没有复用原有CreateView的验证、文件保存逻辑,属于重复开发,且逻辑不全导致提交后无对应反馈
最优改造方案
不用额外新增独立的AJAX视图,直接修改原有CreateProcedureView适配AJAX请求即可,同时修改前端提交逻辑:
1. 前端AJAX代码修改
使用FormData对象处理带文件的表单提交,调整AJAX配置:
$(document).ready(function (){ $("#forms").on('submit', function(event) { event.preventDefault(); // 直接用当前表单构建FormData,自动包含所有字段、文件和CSRF令牌 var formData = new FormData(this); $.ajax({ type: "POST", url: "{% url '你原来的CreateProcedureView对应的路由名' %}", // 无需使用新增的ajax_posting路由 data: formData, datatype: 'json', // 必须添加这两个配置,禁止jQuery处理FormData processData: false, contentType: false, success: function(data) { if (data.success) { alert("提交成功"); // 可自行添加跳转逻辑,比如 location.href = data.redirect_url } else { // 展示表单错误信息,可自行优化展示样式 alert("提交失败:" + JSON.stringify(data.errors)); } }, error: function() { alert("请求异常,请重试"); } }); }); });
2. 原有CreateView改造
修改form_valid和form_invalid方法,适配AJAX请求返回JSON,原有普通提交逻辑完全保留:
from django.http import JsonResponse class CreateProcedureView(LoginRequiredMixin,CreateView): model = NewProcedure form_class = CreateProcedureForm template_name = 'create_procedure.html' def form_valid(self, form): instance = form.save() # 判断是否是AJAX请求 if self.request.headers.get('x-requested-with') == 'XMLHttpRequest': return JsonResponse({ 'success': True, 'redirect_url': reverse('Procedures:procedure_main_menu') }) # 普通提交走原有逻辑 return super().form_valid(form) def form_invalid(self, form): # 判断是否是AJAX请求 if self.request.headers.get('x-requested-with') == 'XMLHttpRequest': return JsonResponse({ 'success': False, 'errors': form.errors.get_json_data() }) # 普通提交走原有逻辑 return super().form_invalid(form) def post(self, request, *args, **kwargs): if "cancel" in request.POST: redirect_url = reverse('Procedures:procedure_main_menu') if request.headers.get('x-requested-with') == 'XMLHttpRequest': return JsonResponse({'success': True, 'redirect_url': redirect_url}) return HttpResponseRedirect(redirect_url) 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) files = request.FILES.getlist('file[]') if form.is_valid() and file_form.is_valid(): procedure_instance = form.save(commit=False) procedure_instance.user = user procedure_instance.save() for f in files: procedure_file_instance = NewProcedureFiles(attachments=f, new_procedure=procedure_instance) procedure_file_instance.save() return self.form_valid(form) else: # AJAX请求时同时返回文件表单的错误 if request.headers.get('x-requested-with') == 'XMLHttpRequest': all_errors = form.errors.get_json_data() all_errors.update(file_form.errors.get_json_data()) return JsonResponse({'success': False, 'errors': all_errors}) return self.form_invalid(form)
注意事项
- 如果你的
CreateProcedureForm初始化时需要传入user参数,请在get_form_kwargs方法中添加,不要直接修改form实例化的参数顺序 - 确保你的表单的
enctype属性设置为multipart/form-data,否则文件无法正常提交
内容的提问来源于stack exchange,提问作者Steve Smith
相关产品推荐
相关产品推荐

