【问题标题】:can you use Form Wizard for Model Forms in django?你可以在 django 中为模型表单使用表单向导吗?
【发布时间】:2011-10-05 00:53:33
【问题描述】:

我有一个模型,并使用ModelForm 从模型中创建了一个表单。现在,我想将表单分布在两个页面上。例如,前三个字段将出现在第一页上,然后用户单击下一步,最后三个字段将出现在第二页上。然后他点击提交,用户提交的数据被添加到数据库中。

我查看了表单向导的文档,它似乎也适用于模型表单?有人可以确认吗?

如果是这样,有人可以解释创建WizardView 类的过程吗?

此示例在文档中给出,我不明白后两个参数是什么。 form_list 只是您根据表单类实例化的表单对象的列表吗? **kwargs 是什么?

class ContactWizard(SessionWizardView):
    def done(self, form_list, **kwargs):
        do_something_with_the_form_data(form_list)
        return HttpResponseRedirect('/page-to-redirect-to-when-done/')

提前感谢您的帮助!

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    假设你的模型有两个字段

    class AModel( Model ):
        fieldA = CharField()
        fieldB = CharField()
    

    我们希望使用FormWizard 在单独的步骤中设置每个字段。所以我们创建了两个ModelForms,每个都显示一个字段:

    class Form1( ModelForm ):
        class Meta:
            model = AModel
            fields = ( 'fieldA', )
    
    class Form2( ModelForm ):
        class Meta:
            model = AModel
            fields = ( 'fieldB', )
    

    我们将表单向导称为AWizardurl.py 条目应该类似于

    url( r'^$', AWizard.as_view( [ Form1, Form2 ] ) ),
    

    AWizard 的实现中,我们需要确保所有表单都将其数据写入单个实例,然后我们将其保存到数据库中:

    class AWizard( SessionWizardView ):
        instance = None
    
        def get_form_instance( self, step ):
            if self.instance is None:
                self.instance = AModel()
            return self.instance
    
        def done( self, form_list, **kwargs ):
            self.instance.save()
    

    请注意,我们重写了方法get_form_instance。此方法返回表单绑定到的模型实例。

    您可能会认为(我确实如此),此方法会为第一个请求(向导的第一步)创建一个实例,然后在所有步骤中继续使用同一个实例。

    实际上,它有点复杂。对于每个请求,都会创建一个新的 AWizard 实例,这反过来又会创建一个新的 AModel 实例。因此,这些步骤一开始并不共享单个实例。

    当最后一个表单被提交时,奇迹发生了。此时所有表单都被重新验证,每个表单调用get_form_instance,它们最终填充一个AModel 实例。

    然后将该实例保存在done 中。

    【讨论】:

    • 这些关于 get_form_instance() 的信息真的很有帮助!谢谢!
    【解决方案2】:

    表单向导已内置到 Django 1.4 中,因此是解决此问题的好方法。它应该可以满足您的需求,但您可能需要进行一些调整。

    现在不用担心done() 中的kwargs - 你不会需要它们。

    form_list 是您要用于步骤的表单列表 - 来自 urls.py

    urlpatterns = patterns('',
        (r'^contact/$', ContactWizard.as_view([ContactForm1, ContactForm2])),
    )
    

    [ContactForm1, ContactForm2] 将作为form_list 传递给done()

    您需要做的是将您的ModelForm 拆分为单独的表格。最简单的方法(如果您希望您的模型在多个表单上)是不使用ModelForm,而只是创建您自己的表单。这很容易:

    from django import forms
    
    class ContactForm1(forms.Form):
        subject = forms.CharField(max_length=100)
        sender = forms.EmailField()
    
    class ContactForm2(forms.Form):
        message = forms.CharField(widget=forms.Textarea)
    

    一旦您的表单反映了模型的各个部分,只需按照docs 中的说明创建viewspatterns,并将do_something_with_the_form_data(form_list) 设置为一个函数,该函数从表单数据完成您的模型,然后执行保存。

    你可以使用ModelForm,但是——只有当你能说服它为表单向导生成不同的表单以用于每个步骤——这将是棘手的部分。

    【讨论】:

    • 如果您选择使用ModelForm,您可以使用fieldsexcludes 来控制每个步骤可见的字段。
    【解决方案3】:

    @wuerg 提出的观点对我不起作用,我不得不这样做:

    class AWizard( SessionWizardView ):
        def dispatch(self, request, *args, **kwargs):
            self.instance = AModel()
            return super(ApplyWizard, self).dispatch(request, *args, **kwargs)
    
        def get_form_instance( self, step ):
            return self.instance
    
        def done( self, form_list, **kwargs ):
            self.instance.save()
            return HttpResponseRedirect(reverse(thanks))
    

    【讨论】:

    • 我想在第一个表单的单选按钮上显示我的表单。我该怎么做?你能指导我吗?
    【解决方案4】:

    我必须更改@wuerg 和@madmen 的解决方案才能在我的用例中工作(每一步都保存模型)。这种方法的一大优点是它总是使用AModel 的同一个实例,而不是为每一步都创建一个新实例:

    class AWizard(SessionWizardView):
        instance = AModel()
    
        def dispatch(self, request, *args, **kwargs):
            return super(AWizard, self).dispatch(request, *args, **kwargs)
    
        def get_form_instance(self, step):
            return self.instance
    
        def done(self, form_list, **kwargs):
            self.save_model()
            return render_to_response('done.html')
    

    【讨论】:

    • 从我的测试来看,这个解决方案不起作用。如果您打开另一个窗口来模拟另一个用户并尝试在向导中导航,您将使用相同的实例,因此,您的字段会预先填充第一个用户的数据!
    猜你喜欢
    • 2021-05-17
    • 1970-01-01
    • 1970-01-01
    • 2014-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-06
    相关资源
    最近更新 更多