Django FormWizard - Passing Data Between Forms

I recently had the need for a multi-step web form and so I wanted to give the Django FormWizard a spin because I haven't had a chance to use it yet.

The FormWizard documentation is lacking a little and it makes no mention of the support for passing an initial value for each form "step" to the Wizard. Passing an initial value is done by passing a dictionary, containing dictionaries, where the keys are the individual form step sequences. For example, the first form will have a key of 0 (zero), the second form will have a key of 1, etc. Here is an example urls.py...


# Import forms and custom wizard
from yourproject.yourapp.forms import *

init = {
0: {
'field1': 'value1',
'field2': 'value2',
},
1: {
'field3': 'value3',
'field4': 'value4',
}
}

# In your urlpatterns...
url(r'^wizard/$', BusinessWizard([Form1, Form2], initial=init),

This will create an instance of the BusinessWizard FormWizard (assuming that's the name of your Wizard) and it will have 2 steps, processing Form1 and Form2. When the wizard is processing the form it will pass the dictionary in key 0 to Form1 as it's initial value and it will iterate with each step.

So what happens if you need data from Form1 to be available in a field in Form2 as the initial value? Well there is no built in way to do this but the good folks over at Django decided to give us a nice helper method that is called from the __call__ method of the FormWizard class.

This helper method is called parse_params and it can be used to help us pass data to the next form by manipulating the FormWizard.initial dictionary.

One thing to note is that parse_params will be called when the form is being displayed and "written to" (via form POST). You need to check for the step PRIOR to the step where you want the data to be passed. If the request method is POST, then you can call the form and check for validation. If valid, you can assign the initial value for the next form based on the cleaned_data dictionary of the current form.

So in my case, I needed the form on step 1 (which is the second form, remember the counter starts at zero) to receive data from step 0.

Here's an example...


from django.contrib.formtools.wizard import FormWizard

class BusinessWizard(FormWizard):
def parse_params(self, request, *args, **kwargs):
current_step = self.determine_step(request, *args, **kwargs)
if request.method == 'POST' and current_step == 0:
form = self.get_form(current_step, request.POST)
if form.is_valid():
self.initial[(current_step + 1)] = {
'name': form.cleaned_data['name'],
'address': form.cleaned_data['address'],
'city': form.cleaned_data['city'],
}

def done(self, request, form_list):
# Your form processing code goes here

When the second form is displayed it will have the initial value assigned in the parse_params method.

I can't think of many situations where you would want to do this but they do pop up occasionally. If you are curious about the FormWizard class check the source code out. It's a small class and very easy to follow.

Note: This might not be the "best" way to do this. The process_step method may also be a good place for this type of operation but it was called twice, conditionally, in the __call__ method so it just felt "right" to use parse_params.