可以在Django中对模型表单使用表单向导吗?
我有一个模型,然后我用这个模型创建了一个表单,使用的是 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 个回答
@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))
假设你的模型有两个字段
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
中被保存。
在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)
一旦你的表单反映了模型的不同部分,就按照文档中的说明创建views
和patterns
,并将do_something_with_the_form_data(form_list)
设置为一个函数,这个函数会根据表单数据完成你的模型并保存。
你可以使用ModelForm
,但前提是你能让它为每一步生成不同的表单,这部分会比较棘手。