可以在Django中对模型表单使用表单向导吗?

12 投票
4 回答
5740 浏览
提问于 2025-04-16 21:29

我有一个模型,然后我用这个模型创建了一个表单,使用的是 ModelForm。现在,我想把这个表单分成两页来显示。比如,前面三个字段在第一页显示,用户点击“下一步”,然后最后三个字段在第二页显示。最后,用户点击提交,填写的数据就会被添加到数据库里。

我查看了一下关于表单向导(Form Wizard)的文档,似乎它也适用于模型表单?有人能确认一下吗?

如果可以的话,能不能有人解释一下如何创建一个 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/')

提前谢谢你的帮助!

4 个回答

2

@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))
25

假设你的模型有两个字段

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

我们想要通过一个叫做 FormWizard 的工具,把每个字段分开一步一步地设置。所以我们创建了两个 ModelForm,每个表单只显示一个字段:

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

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

我们把这个表单向导叫做 AWizard;在 url.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 中被保存。

6

在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)

一旦你的表单反映了模型的不同部分,就按照文档中的说明创建viewspatterns,并将do_something_with_the_form_data(form_list)设置为一个函数,这个函数会根据表单数据完成你的模型并保存。

你可以使用ModelForm,但前提是你能让它为每一步生成不同的表单,这部分会比较棘手。

撰写回答