Ok, I've found a solution that does not use ajax at all and seems nice enough to me :
Create as many forms as needed and make them subclass each other. Put an Integer Hidden Field into the first one :
class Form1(forms.Form):
_nextstep = forms.IntegerField(initial = 0, widget = forms.HiddenInput())
foo11 = forms.IntegerField(label = u'First field of the first form')
foo12 = forms.IntegerField(label = u'Second field of the first form')
class Form2(Form1):
foo21 = forms.CharField(label = u'First field of the second form')
class Form3(Form2):
foo31 = forms.ChoiceField([],
label=u'A choice field which choices will be completed\
depending on the previous forms')
foo32 = forms.IntegerField(label = u'A last one')
# You can alter your fields depending on the data.
# Example follows for the foo31 choice field
def __init__(self, *args, **kwargs):
if self.data and self.data.has_key('foo12'):
self.fields['foo31'].choices = ['make','a','nice','list',
'and you can','use your models']
Ok, that was for the forms now here is the view :
def myview(request):
errors = []
# define the forms used :
steps = [Form1,Form2,Form3]
if request.method != 'POST':
# The first call will use the first form :
form = steps[0]()
else:
step = 0
if request.POST.has_key('_nextstep'):
step = int(request.POST['_nextstep'])
# Fetch the form class corresponding to this step
# and instantiate the form
klass = steps[step]
form = klass(request.POST)
if form.is_valid():
# If the form is valid, increment the step
# and use the new class to create the form
# that will be displayed
data = form.cleaned_data
data['_nextstep'] = min(step + 1, len(steps) - 1)
klass = steps[data['_nextstep']]
form = klass(data)
else:
errors.append(form.errors)
return render_to_response(
'template.html',
{'form':form,'errors':errors},
context_instance = RequestContext(request))
The only problem I saw is that if you use {{form}} in your template, it calls form.errors and so automagically validates the new form (Form2 for example) with the data of the previous one (Form1). So what I do is iterate over the items in the form and only use {{item.id}}, {{item.label}} and {{item}}. As I've already fetched the errors of the previous form in the view and passed this to the template, I add a div to display them on top of the page.