Form preview

Django comes with an optional “form preview” application that helps automate the following workflow:

“Display an HTML form, force a preview, then do something with the submission.”

To force a preview of a form submission, all you have to do is write a short Python class.


Form preview doesn’t work with file uploads.


Given a Form subclass that you define, this application takes care of the following workflow:

  1. Displays the form as HTML on a Web page.
  2. Validates the form data when it’s submitted via POST. a. If it’s valid, displays a preview page. b. If it’s not valid, redisplays the form with error messages.
  3. When the “confirmation” form is submitted from the preview page, calls a hook that you define – a done() method that gets passed the valid data.

The framework enforces the required preview by passing a shared-secret hash to the preview page via hidden form fields. If somebody tweaks the form parameters on the preview page, the form submission will fail the hash-comparison test.

How to use FormPreview

  1. Point Django at the default FormPreview templates. There are two ways to do this:

    • Add 'formtools' to your INSTALLED_APPS setting.

      This will work if your TEMPLATES setting includes the app_directories template loader (which is the case by default).

      See the template loader docs for more.

    • Otherwise, determine the full filesystem path to the formtools/templates directory and add that directory to your DIRS option in the TEMPLATES setting.

  2. Create a FormPreview subclass that overrides the done() method:

    from django.http import HttpResponseRedirect
    from formtools.preview import FormPreview
    from myapp.models import SomeModel
    class SomeModelFormPreview(FormPreview):
        def done(self, request, cleaned_data):
            # Do something with the cleaned_data, then redirect
            # to a "success" page.
            return HttpResponseRedirect('/form/success')

    This method takes an HttpRequest object and a dictionary of the form data after it has been validated and cleaned. It should return an HttpResponseRedirect that is the end result of the form being submitted.

  3. Change your URLconf to point to an instance of your FormPreview subclass:

    from django import forms
    from myapp.forms import SomeModelForm
    from myapp.preview import SomeModelFormPreview

    …and add the following line to the appropriate model in your URLconf:

    path('post/', SomeModelFormPreview(SomeModelForm)),

    where SomeModelForm is a Form or ModelForm class for the model.

  4. Run the Django server and visit /post/ in your browser.

FormPreview classes

class formtools.preview.FormPreview[source]

A FormPreview class is a simple Python class that represents the preview workflow. FormPreview classes must subclass FormPreview and override the done() method. They can live anywhere in your codebase.

FormPreview templates


By default, the form is rendered via the template formtools/form.html, and the preview page is rendered via the template formtools/preview.html.

These values can be overridden for a particular form preview by setting preview_template and form_template attributes on the FormPreview subclass. See formtools/templates for the default templates.

Required methods

FormPreview.done(request, cleaned_data)[source]

Does something with the cleaned_data data and then needs to return an HttpResponseRedirect, e.g. to a success page.

Optional methods


Hook to override the auto_id kwarg for the form. Needed when rendering two form previews in the same template.


Takes a request argument and returns a dictionary to pass to the form’s initial kwarg when the form is being created from an HTTP get.

FormPreview.get_context(request, form)[source]

Context for template rendering.

FormPreview.parse_params(request, *args, **kwargs)[source]

Given captured args and kwargs from the URLconf, saves something in self.state and/or raises Http404 if necessary.

For example, this URLconf captures a user_id variable:

path('contact/<int:user_id>/', MyFormPreview(MyForm)),

In this case, the kwargs variable in parse_params would be {'user_id': 32} for a request to '/contact/32/'. You can use that user_id to make sure it’s a valid user and/or save it for later, for use in done().

FormPreview.process_preview(request, form, context)[source]

Given a validated form, performs any extra processing before displaying the preview page, and saves any extra data in context.

By default, this method is empty. It is called after the form is validated, but before the context is modified with hash information and rendered.

FormPreview.security_hash(request, form)[source]

Calculates the security hash for the given HttpRequest and Form instances.

Subclasses may want to take into account request-specific information, such as the IP address.


Returns an HttpResponse in the case of an invalid security hash.