can you use Form Wizard for Model Forms in django?
I have one model and I've created a form out of the model using ModelForm
. Now, I want to spread the form across two pages. For example, the first three fields will appear on the first page then the user clicks next and the last three fields appear on the second page. Then he clicks submit and the user submitted data is added to the database.
I took a look at the docs for the Form Wizard and it seems like it would work for model forms as well? Can someone confirm this?
And if it does, can someone explain the process of creating a WizardView
class.
This example is given in the docs and I don't understand what the second two parameters are. Is form_list just a list of form objects that you've instantiated based on your form classes? And what is **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/')
Thanks in advance for your help!
Say your model has two fields
class AModel( Model ):
fieldA = CharField()
fieldB = CharField()
We want to set each field in a separate step using a FormWizard
. So we create two ModelForm
s, each showing one field:
class Form1( ModelForm ):
class Meta:
model = AModel
fields = ( 'fieldA', )
class Form2( ModelForm ):
class Meta:
model = AModel
fields = ( 'fieldB', )
We call our form wizard AWizard
; the url.py
entry should look something like
url( r'^$', AWizard.as_view( [ Form1, Form2 ] ) ),
In the implementation of AWizard
we need to make sure all the forms write their data to a single instance, which we then save to the database:
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()
Notice that we override the method get_form_instance
. This method returns the model instance the forms bind to.
You might think (I did), that this method creates an instance for the first request (the first step of the wizard), and then keeps using that same instance for all steps.
Actually, it's a little more complicated. For each request a new instance of AWizard
is created, which in turn creates a new AModel
instance. So, the steps don't share a single instance to start with.
The magic happens when the last form is submitted. At this point all forms are revalidated, each form calls get_form_instance
and they end up populating a single AModel
instance.
That instance is then saved in done
.
Form Wizard is being built into Django 1.4 so is a good way to go about this. It should do what you want, but you may need a couple of tweaks.
Don't worry about the kwargs
in done()
at the moment - you're not going to need them.
form_list
is the list of forms that you want to use for your steps - from urls.py
urlpatterns = patterns('',
(r'^contact/$', ContactWizard.as_view([ContactForm1, ContactForm2])),
)
[ContactForm1, ContactForm2]
will be passed to done()
as form_list
.
What you will need to do is break your ModelForm
into separate forms. The easiest way to do this (if you want your model on several forms) is to not use ModelForm
but just create your own form. It's pretty easy:
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)
Once your forms reflect the portions of your model, just create the views
and patterns
as described in the docs and set do_something_with_the_form_data(form_list)
to a function that completes your model from the form data and then does a save.
You could use ModelForm
but - only if you can persuade it to produce different forms for Form Wizard to use for each step - that's going to be the tricky part.
The view proposed by @wuerg did not work for me, I had to do this:
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))
I had to alter the solution of @wuerg and @madmen to work in my usecase (saving the Model after every step). The big advantage of this approach is that it always uses the same instance of the AModel
instead of creating a new instance for every step:
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')
精彩评论