如何在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
相关产品推荐
相关产品推荐

