2

At the moment I'm experimenting with django form wizard. The basic set-up works now and I'm able to call different templates for each step showing variable text.

Now I want to take it a step further and create a customized form layout for each step. The documentation of Django shows a generic way to show the form, always with vertical alignment.

In my experiment I have two steps:

  • step 1: email and password (just two fields needing vertical alignment)
  • step 2: personal data: address, profession, ...

So for the step 2 I want to use a complete different form layout then step 1: using fieldsets, horizontal alignment of field (eg address: street and number), ...

Starting from the django documentation I suppose that the below can work (did not test it yet):

{% block content %}
# block step: variable text for each step
{% block step %}{% endblock %}
<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<form action="" method="post">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
    {{ wizard.form.management_form }}
    {% for form in wizard.form.forms %}
        # block form_if: shows a complete customized form layout for each step
        {% block form_if %}{% endblock %}
    {% endfor %}
{% else %}
    # block form_else: shows a complete customized form layout for each step
    {% block form_else %}{% endblock %}
{% endif %}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">{% trans    "first step" %}</button>
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% trans "prev step" %}</button>
{% endif %}
<input type="submit" value="{% trans "submit" %}"/>
</form>
{% endblock %}

But the problem I have here is that I have two blocks: form_if and form_else calling the same form layout. So then I have double maintenance of my form layout.

Are there better ways to accomplish what I want to achieve?

Thanks!

Kind Regards

Köver
  • 371
  • 4
  • 12
  • If you show only one form on each step then you need form_else only. In this case you can significantly simplify your template. Can't you? – sergzach Aug 01 '12 at 23:42

4 Answers4

2

Based on the input of Rohan and following post, I found a working solution:

my base_wizard template looks as follows:

<form enctype="multipart/form-data" action="" method="post">{% csrf_token %}
    <table>
        {{ wizard.management_form }}
        {% if wizard.form.forms %}
            {{ wizard.form.management_form }}
            {% for form in wizard.form.forms %}
                <!-- block below accesses a customized form layout for each step -->
                {% block form_if %}{% endblock %}
            {% endfor %}
        {% else %}
            <!-- block below accesses a customized form layout for each step -->
            <!-- using the with statement makes it possible to use the same layout used in the form_if block -->
            {% with form=wizard.form %}
            {% block form_else %}{% endblock %}
            {% endwith %}
        {% endif %}
    </table>
    {% if wizard.steps.prev %}
    <button name="wizard_goto_step" value="{{ wizard.steps.first }}" class="button">{% trans "first step" %}</button>
    <button name="wizard_goto_step" value="{{ wizard.steps.prev }}" class="button">{% trans "prev step" %}</button>
    {% endif %}
    <div>
        <input type="submit" value="{% trans "submit" %}" class="button"/>
    </div>  
</form>

and in the template per step I use following code:

{% extends "base_wizard.html" %}

{% block form_if %}{% block form_else %}
    <fieldset>
        <legend>Title</legend>
        <div>
            <label>{{ form.field.label }}:<p class="note">{{ form.field.help_text }}</p></label> {{ form.field }}
            <p class="error">
                {% if form.field.errors %}
                    {% for error in form.field.errors %}
                        {{ error }}
                    {% endfor %}
                {% endif %}
            </p>
        </div>
    </fieldset>
{% endblock %}{% endblock %}

Using this makes it possible to maintain just one form layout per step instead of two.

Community
  • 1
  • 1
Köver
  • 371
  • 4
  • 12
1

You can create different templates for each steps. The templates can produce html as you want.

To tell django to use different form templates for steps, you can follow this...

class TestFormWizard(SessionWizardView):

    def get_template_names(self):
        if self.steps.current == 1:
            return 'step1_form.html'
        if self.steps.current == 2:
            return 'step2_form.html'
        return 'wz_form.html'

You can define step1_form.html, step2_form.html etc. as you want. Tip: For common code in templates (ie. management form fields), create different templates and include them in per step forms.

Rohan
  • 52,392
  • 12
  • 90
  • 87
  • Hi Rohan, thx already for the feedback. Indeed I already call a different template for each step. Each is filling the `block form_if` and `block form_else` tag in my code with a specific form. But for the moment I copy the form two times in my template (one for each block), because I can't use the same block twice in the template => gives errors. With double maintenance as a consequence.If you would have a solution for that, I would appreciate it :) Cheers! – Köver Jun 23 '12 at 18:37
1

May be in that case you can make use of 'with' statement in templates. Something like: in your block_if and block_else refer form as myform.

{{ wizard.management_form }}
{% if wizard.form.forms %}
    {{ wizard.form.management_form }}
    {% for form in wizard.form.forms %}
        # block form_if: shows a complete customized form layout for each step
        {% with myform=form %}
        {% block form_if %}{% endblock %}
        {%e endwith %}
    {% endfor %}
{% else %}
    # block form_else: shows a complete customized form layout for each step
    {% with myform=form %}    
    {% block form_else %}{% endblock %}
    {% endwith %}
{% endif %}
Rohan
  • 52,392
  • 12
  • 90
  • 87
0

Try giving crispy-forms a look. In a nutshell, you'll have a helper instance attribute set on your forms that can pretty much control most of what you'd ever want to accomplish with structuring a form without touching the template code.

In certain cases, customizing a form's presentation makes for unwieldy templates if you end up having to write the whole markup by hand yourself. Here you can isolate and structure the mess in a form_helpers.py module in the application.

If you can work with what crispy-forms provides in terms of creating the form layout, you'll see that about 10 lines of code you need to defined the form helper's layout rids you of dozens and dozens of lines of template code you'd otherwise have to maintain.

What you would end up in your case is having multiple forms, a form helper instance for each specific form and a single template that just calls a crispy-form tag to render the form the wizard supplied for each step.

Filip Dupanović
  • 32,650
  • 13
  • 84
  • 114
  • Hi Filip, thx for the input. Not exactly the answer I was looking for, but I definitely will check this app out. Thanks! – Köver Jun 23 '12 at 18:44
  • and how would you implement it? I tried to crispyfy the wizard but the helper did not help. – headkit Feb 03 '22 at 16:24