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

如何在Django Formwizard中使用Formset并处理其提交数据

Django FormWizard集成Formset数据获取解决方案

问题根因

你当前的实现仅将Formset传入了模板上下文,没有纳入FormWizard的表单验证、数据持久化存储流程,所以done()方法无法获取到提交的Formset数据。同时还存在几处可优化的代码问题:

  • createProductAttributeForm同时继承forms.ModelForm和forms.Form属于多余继承,保留ModelForm即可
  • 操作模型数据的表单集建议使用modelformset_factory而非普通formset_factory,更适配模型数据的增删改场景
  • 编辑场景下步骤1的ProductAtribute查询使用get()仅能返回单条数据,不符合多属性的业务场景

修改步骤

1. 表单代码调整

# 去掉多余的forms.Form继承
class createProductAttributeForm(forms.ModelForm):
    class Meta:
        model = ProductAtribute
        widgets = {
            'image': forms.FileInput(),
        }
        fields = ['image','sku','price','offer_price','attribute','attribute_value']

# 改用modelformset_factory,支持编辑场景下的模型数据绑定
from django.forms import modelformset_factory
ProductAttributeFormset = modelformset_factory(ProductAtribute, form=createProductAttributeForm, extra=2, can_delete=True)

2. 视图代码调整

首先覆盖get_form()方法,让步骤1返回Formset实例而非普通表单

class createProductWizard(SessionWizardView):
    # ... 原有其他代码保持不变
    def get_form(self, step=None, data=None, files=None):
        step = step or self.steps.current
        if step == '1':
            # 继承FormWizard的前缀逻辑,避免表单字段名冲突
            formset_class = ProductAttributeFormset
            prefix = self.get_form_prefix(step, formset_class)
            # 编辑场景下传入queryset绑定已有数据
            queryset = ProductAtribute.objects.none()
            if 'id' in self.kwargs:
                queryset = ProductAtribute.objects.filter(product_id=self.kwargs['id'])
            form = formset_class(data=data, files=files, prefix=prefix, queryset=queryset)
            return form
        return super().get_form(step, data, files)

覆盖process_step()方法,将验证后的Formset数据存入Wizard存储

def process_step(self, form):
        if self.steps.current == '1':
            # 保存formset的清理后数据到storage
            if form.is_valid():
                self.storage.set_step_data(self.steps.current, {
                    'formset_cleaned_data': [f.cleaned_data for f in form if f.cleaned_data and not f.cleaned_data.get('DELETE', False)]
                })
        return super().process_step(form)

修改done()方法,获取Formset数据并保存

def done(self,form_list,**kwargs):
        try:
            if self.kwargs.get('id'):
                product_instance=Product.objects.get(id=self.kwargs['id'])
            else:
                product_instance = Product()
        except Exception as e:    
            product_instance = Product()

        # 保存基础产品和SEO数据
        for form in form_list:
            product_instance = construct_instance(form, product_instance, form._meta.fields, form._meta.exclude)
        product_instance.save()

        # 获取步骤1的Formset数据并保存
        step1_data = self.storage.get_step_data('1')
        if step1_data and 'formset_cleaned_data' in step1_data:
            # 编辑场景下先删除旧数据(也可以做增量更新,按需选择)
            ProductAtribute.objects.filter(product=product_instance).delete()
            for attr_data in step1_data['formset_cleaned_data']:
                # 去掉id字段避免新建冲突
                attr_data.pop('id', None)
                ProductAtribute.objects.create(product=product_instance, **attr_data)
        
        return redirect('list')

3. 模板适配

在step2-formset.html中必须渲染Formset的管理表单,否则提交数据会识别不完整:

{{ formset.management_form }}
{% for form in formset %}
    {{ form.as_p }}
{% endfor %}

内容的提问来源于stack exchange,提问作者aneesh .p.k

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 23:39:03