Search code examples
pythondjangoformsdjango-formwizard

Save form data on every step using Django FormWizard


Background

I'm building a very large form to process customer submissions, so the end goal is to allow the user to resume the form where they left off at a later date. The form is fully functional using a FormWizard (NamedUrlSessionWizardView, actually). The Django docs mention a final save is accomplished in the done method, and leave this as an exercise to the reader. This works OK if the user completes this in one sitting, but not if you want to restore this later.

In my case, an email address is used to lookup past progress, and send a unique link to the user. This sets up the form and returns the user to where they left off. This works fine as long as your session is still valid, but not if it isn't (different computer, etc). What I would like to do is save the form data (these are ModelForms) after each step. I'll restore the user's state when they return.

Research

This answer is about the only solution I can find, but the solution is the same thing that the standard FormWizard.post() method does:

if form.is_valid():
    # if the form is valid, store the cleaned data and files.
    self.storage.set_step_data(self.steps.current, self.process_step(form))
    self.storage.set_step_files(self.steps.current, self.process_step_files(form))

My Question

What is the proper way/place in a FormWizard to take action on, and save the form data out after each step?


Solution

  • You should be able to save the data directly to the ModelForm as you go along by simply writing it into the post method.

    if self.steps.current == "form1":
        data = self.request.POST["form1-response"]
        user = CustomerModel.objects.get(id=self.request.user.id)
        user.response = data
        user.form_step = "form1"
        user.save()
    

    form_step, in this case, is simply a bookmark that you can use to direct the user back to the right step on their return. You should remove any already-saved fields from the done method, so they don't get overwritten.

    If you do it this way, you may need to construct a dispatch method that rebuilds the management form when the user logs back in.

    Alternatively, you might be able to get away with saving the user's session (or the relevant parts) into a session field on the model, then write a dispatch method for the SessionWizardView that injects the relevant information back in. I've never attempted it, but if you can get it to work, it might be preferable from an aesthetic standpoint depending on how many steps you have to cover.

    Finally, if you can rely on your users not to clear their cookies and to use the same browser when they return, you can maybe cheat and set use persistent cookies.

    Hopefully that will get you started. I'd be interested to see how you end up getting it to work. Good luck!