6👍
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.
25👍
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
.
- Signal/Method for every executed sql statement
- How to limit choices of ForeignKey choices for Django raw_id_field
- Are there any good options for baking out a Django site as static files?
- Double curly brace {{
2👍
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))
- How to rename a foreignkey field with South?
- Django. ''The `actions` argument must be provided when calling `.as_view()` '' when I try to allow DELETE, PUT, ETC
- No module named rest_auth
- Django Full Text SearchVectorField obsolete in PostgreSQL
-1👍
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')