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

Django Form Wizard 首步FileField提交后数据库为空如何解决

问题根因

你遇到的文件字段为空问题核心有3个错误点:

  • 全局变量存表单数据逻辑错误:你自定义的全局form_data2仅收集了普通POST表单字段,文件属于request.FILES范畴的二进制数据,不会出现在get_form_step_data()返回的普通字段中,所以form_data2里根本不存在file键值对,入库时自然没有值。
  • 跳转步骤的文件存储逻辑错误:render_goto_step方法中你硬编码将当前步骤的文件覆盖存储到第一步的文件槽位,若用户跳转过步骤会导致第一步上传的文件被清空。
  • 多线程安全问题:Django服务是多线程运行的,全局变量form_data、form_data2会在多个用户同时提交时串数据,属于严重的逻辑漏洞。
修复方案

1. 修正跳转步骤的文件存储逻辑

修改render_goto_step方法,将文件存储到当前步骤对应的槽位:

def render_goto_step(self, *args, **kwargs):
    form = self.get_form(data=self.request.POST, files=self.request.FILES)
    self.storage.set_step_data(self.steps.current, self.process_step(form))
    # 原逻辑错误:把所有步骤的文件都存到第一步,改为存到当前步骤
    self.storage.set_step_files(self.steps.current, self.process_step_files(form))
    return super().render_goto_step(*args, **kwargs)

2. 重写done方法,废弃全局变量

不要手动拼接表单数据,直接从form_list中取每一步的cleaned数据,文件字段直接从第一步的CompanyForm中获取:

def done(self, form_list, **kwargs):
    try:
        # 第一步就是CompanyForm,直接取cleaned_data
        company_data = form_list[0].cleaned_data
        address_data = form_list[1].cleaned_data
        payment_data = form_list[4].cleaned_data
        
        # 合并基础数据
        customer_data = {**company_data, **address_data, **payment_data}
        
        # 处理发票地址逻辑
        if not customer_data.get('different_invoice_address'):
            customer_data['invoice_street'] = customer_data.get('delivery_street')
            customer_data['invoice_zip'] = customer_data.get('delivery_zip')
            customer_data['invoice_city'] = customer_data.get('delivery_city')
            customer_data['invoice_house_number'] = customer_data.get('delivery_number')
        
        # 移除不需要的字段
        for remove_key in ['csrfmiddlewaretoken', 'register_wizard-current_step', 'wizard_goto_step', 'toa', 'using_mail_address', 'different_invoice_address']:
            if remove_key in customer_data:
                customer_data.pop(remove_key)
        
        # 直接创建Customer,file字段会自动从company_data中读取并保存到正式路径
        customer = Customer.objects.create(**customer_data)
        
        # 保存关联表单集
        for reference in form_list[2].save(commit=False):
            reference.customer = customer
            reference.save()
        
        for contact in form_list[3].save(commit=False):
            contact.customer = customer
            contact.save()
    
    except Exception as e:
        logger.error(e)
    finally:
        return HttpResponseRedirect('/thank-you')

3. 清理冗余代码

删除全局的form_data、form_data2变量,以及process_step方法中操作这两个全局变量的逻辑,你不需要手动收集表单数据,formtools的wizard已经帮你把所有步骤的表单数据、文件都存在storage里了。


内容的提问来源于stack exchange,提问作者Samoht

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 01:54:01