Formless Step in FormWizard
2 min read

Formless Step in FormWizard

Formless Step in FormWizard

Sometimes you'd need to show a purely informational page as part of a FormWizard. As far as I could see, there are two solutions to the problem:

  1. If the info is something like a summary, you have it available in the "success" page (the one after the wizard is finished)
  2. Alternatively, you can make it a page in the wizard.

The more challenging and versatile variant is the second one (versatile because it gives you the possibility to go back to a previous step to correct stuff).

My Solution

My solution is somewhat of an abuse of the FormWizard functionality (but perfectly legal, I assure you).

A Form

First, each step must have a form. So, you need to assign a form class to the "empty form" step. I did it by creating an empty form:

class EmptyForm(forms.Form):
    """
    A form to show an information panel.
    """
    pass

This form needs to be added to your list of steps:

class ConfigurationWizard(SessionWizardView):
    """
    Configuration wizard
    """
    template_name = "configuration/base.html"
    form_list = [
        (STEP_ONE, ToolForm),     #
        (STEP_UID, IDForm),       #
        (STEP_CONFIG, EmptyForm)  # <- This is the info panel
    ]

Build Data

Once we've built the list of steps, and we have our panel, we need to build some data we can use. To this effect, we use the standard django view approach via context variables. The FormWizard provides us with a context method which we overload:

def get_context_data(self, form, **kwargs):
    """
    Get custom context data for the config step
    """
    context = super(ConfigurationWizard, self).get_context_data(
        form=form, **kwargs
    )

    # If we have the CONFIG step, then build the context data from
    # the previous ones. This way, we can have a variable render-able
    # directly in a template.
    #
    if self.steps.current == STEP_CONFIG:
        tool = "%s" % self.get_cleaned_data_for_step(STEP_TOOL)[
            'tool'].get_slug()
        key = "%s" % self.get_cleaned_data_for_step(STEP_UID)['id']

        context.update({
            'tool': tool,
            'key': key
        })
    return context

Using the Data

Cool. Now we have the data available. To use it, we only need to load a custom template. Fortunately, FormWizard allows loading different templates for different steps. To do this, I added a dict of custom templates:

class ConfigurationWizard(SessionWizardView):
    # ...
    # ...
    # ...
    templates = {
        STEP_CONFIG: "config/form-config.html"
    }

and overloaded the get_template_names() method:

def get_template_names(self):
    """
    Allow retrieving custom templates for different steps.
    :return: an array with the relevant template for the current step
    """
    try:
        t_names = [ConfigurationWizard.templates[self.steps.current]]
        return t_names

    except KeyError, e:
        default_template = super(ConfigurationWizard,
                                  self).get_template_names()
        return default_template

My template looks like this:

{% extends "config/base.html" %}
{% load staticfiles i18n %}
{% load url from future  %}


{% block content %}
    <h3>Configuration</h3>
    <p>Your configuration data is:</p>
    {% spaceless %}
    <pre>
        <code class="json">{
            "tool": "{{ tool }}",
            "key": "{{ key }}",
        }</code>
    </pre>
    {% endspaceless %}
    <p>Copy it or</p>
    <a class="btn btn-default"
        href="{% url 'download_config' %}?key={{ key }}&tool={{ tool }}">
        Download <strong>your configuration file</strong>
    </a>
{% endblock %}

Summary

To make this panel work you need to:

  1. Create an empty form and add it to a FormWizard step
  2. Build a template context for the relevant step
  3. Build a custom template and load it

HTH,