Django form wizard - customized form layout for each step - django

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

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.

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.

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 %}

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.

Related

Why does template inheritance in Django not show error?

I'm not seeing an error when template inheritance seems to fail. I don't see content from the child template but I don't see a 'TemplateNotFound' error. If I change the path to the parent template to 'notthefilename.html' I still don't see an error. Is there a way to make template inheritance fail noisily? I have no idea why this is not working even though I have a similar inheritance in a adjacent folder that is working as expected.
generic_create.html The text 'hello2' is rendering.
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div style= "padding-top: 30px">
<form method="post">{% csrf_token %}
{% if model_name == 'TransactionsTable' %}
<div>
{% block transactions_create %}
hello2
{% endblock transactions_create %}
</div>
{% else %}
{{ form |crispy }}
{% endif %}
<div id="container">
<input type="submit" class="btn btn-default" value="Save">
Cancel
<div>
</form>
</div>
{% endblock content %}
transactions_create.html - The content 'Hello1' is not rendering
{% extends "generic_create.html" %}
{% load static %}
{% block transactions_create %}
Hello1
{% endblock transactions_create %}
Thanks to #Daniel Roseman's comment I realized I was performing the inheritance backwards. I was also over-complicating things. Using the include tag I was able to perform the sort of inheritance I was hoping to achieve very easily.
generic_create.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div style= "padding-top: 30px">
<form method="post">{% csrf_token %}
{% if model_name == 'TransactionsTable' %}
<div>
{% include "transactions_create.html" %} #NOTE: Here I removed the block and added 'include'.
</div>
{% else %}
{{ form |crispy }}
{% endif %}
<div id="container">
<input type="submit" class="btn btn-default" value="Save">
Cancel
<div>
</form>
</div>
{% endblock content %}
transactions_create.html
Hello1 #removed all of the extend and 'block' code. 'include' just renders the html directly.
That's it! 'include' really simplified things.

ListField is showing <ul> instead of <input> in edit/create post

I am using Flask, mongoengine for a project and I am trying to get basic stuff working from http://docs.mongodb.org/manual/tutorial/write-a-tumblelog-application-with-flask-mongoengine/
After implementing everything from above link I added a new field for "tags" in Post and when I try to create a post, my tags doesn't show a input box.
Any help is appreciated.
My code and screenshot below
class Post(db.DynamicDocument):
created_at = db.DateTimeField(default=datetime.datetime.now, required=True)
title = db.StringField(max_length=255, required=True)
slug = db.StringField(max_length=255, required=True)
comments = db.ListField(db.EmbeddedDocumentField('Comment'))
tags = db.ListField(db.StringField(max_length=30)) # New field I added
template form
{% macro render(form) -%}
<fieldset>
{% for field in form %}
{% if field.type in ['CSRFTokenField', 'HiddenField'] %}
{{ field() }}
{% else %}
<div class="clearfix {% if field.errors %}error{% endif %}">
{{ field.label }}
<div class="input">
{% if field.name == "body" %}
{{ field(rows=10, cols=40) }}
{% else %}
{{ field() }}
{% endif %}
{% if field.errors or field.help_text %}
<span class="help-inline">
{% if field.errors %}
{{ field.errors|join(' ') }}
{% else %}
{{ field.help_text }}
{% endif %}
</span>
{% endif %}
</div>
</div>
{% endif %}
{% endfor %}
</fieldset>
{% endmacro %}
rendering form code
{% extends "admin/base.html" %}
{% import "_forms.html" as forms %}
{% block content %}
<h2>
{% if create %}
Add new Post
{% else %}
Edit Post
{% endif %}
</h2>
<form action="?{{ request.query_string }}" method="post">
{{ forms.render(form) }}
<div class="actions">
<input type="submit" class="btn primary" value="save">
Cancel
</div>
</form>
{% endblock %}
From what I can gather, your problem is you're telling WTF to render the tags field, but WTForms doesn't know how to handle that information.
From looking at the Flask-MongoEngine documentation, it seems the ListField is just a FieldList as WTForms refers to it.
Currently you're not actually defining the form independently in WTForms, you're just using the magic included in Flask-MongoEngine, so my first attempt would be to add some more logic to your macro, add a {% elif field.type == 'ListField' %} and try and discover what's contained in there to iterate through to produce your form. From having a quick look at the source-code, something like the following might work.
{% elif field.type == 'ListField %}
{# render_the_group_label #}
{% for subfield in field.entries %}
{% if subfield.type == 'StringField' %}
{# render_the_subfield #}
{% endif %}
{% endfor %}
...
That code will need to be worked on, but hopefully it'll point you in the right direction. Otherwise, I'd actually define the form seperately in WTForms to give you a bit more control on the code-side. Luckily they provide a csv tag example which should help you if you need to go that route. I wrote a guide that takes a different route using #property decorators to achieve a similar effect, which again, might at least point you towards the finish line.

Django admin add custom button in change form depending on a form field value

I have an application that overrides het Django change form. What I want to change is the submit buttons on the bottom. Underneath is change_form.html in a django app:
{% extends "admin/change_form.html" %}
{% block submit_buttons_bottom %}
## add some buttons
{% endblock %}
The button I want to show/add depends on the value of a certain field in the form names 'status'. How can I get the value of a field in the template... something like:
{% if form.field.status == 'unresolved' %}
<input type="submit" value="Mark as resolved" class="default" name="_save" />
{% endif %}
UPDATE:
I'm not getting any errors. There is simply nothing displayed.
Looping through var 'adminform' will get me to the field I need
{% for fieldset in adminform %}
{% for line in fieldset %}
{% for field in line %}
{% if field.field.name == 'status' %}
this is status {{ field.field.name }} - {{ field.contents }}
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
But I want to access it directly. Something like:
{% if adminform.0.0.field.status == 'unresolved' %}
<input type="submit" value="Mark as resolved" class="default" name="_save" />
{% endif %}
This should work:
{% if adminform.form.status.value == 'unresolved' %}
or if the field is readonly, there's another way:
{% if adminform.form.instance.status == 'unresolved' %}
Try changing your if statement to -
{% if adminform.status.value == 'unresolved' %}
I'm guessing, but I think the adminform variable is probably just a form. Have a look at this section of the docs to see the atributes of the form fields.

How to change the layout for templates in a Django Form-Wizard?

I am using Django's Form Wizard package to create a 4-step form.
I have created forms for each of the four steps. It's working fine. In my case I have created 4 separate templates and each step of the form wizard uses its own template.
But each of these templates that I have created is substantially the same. Below is the code that is in each one. I cut and pasted this from elsewhere. It works, but I don't fully understand how or why:
<form action="" method="post">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
X
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
Y
{{ wizard.form }}
{% endif %}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% trans "Previous Step" %}</button>
{% endif %}
<input type="submit" value="{% trans "Next Step" %}"/>
</form>
What I need to do is alter the layout for the form in step #2 of 4. I don't want the form's input elements to be stacked on top of each other as they currently are. I want do something a little different by manipulating the the .
But I don't know how to do this. Can someone show me a template in which they are using the actual named form elements instead of just {{wizard.form}}? Keep in mind, if there is a validation error, I still need that to show up on this webpage. How to accomplish all that? A simple example of what your template looks like would be ideal.
If you want to control the rendering of your forms you should render the form fields manually. Your forms are still standard Django forms after all.
But each of these templates that I have created is substantially the
same.
You certainly shouldn't repeat the same template for each step.
It would be cleaner to use your template as a base template instead and extend this base template in the templates you provide for the steps where you want to customize the layout. That way you could even easily embed extra CSS or JS for certain steps.
So instead of
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
you would create the following block in your base template
{% block form %}
{{ wizard.form }}
{% endblock %}
That way you can customize the rendering of your forms for certain steps by simply overriding the form block.
{% extends "base_step.html" %}
{% block form %}
{{ wizard.form.example_field_to_show }}
...
{% endblock %}

Trying to access ModelForm field modelChoice choices in Django template

I'm generating ModelForms and want some granular control over how they are output in my template. Specifically, I need to add some markup to the end of each radio button in each of my select lists.
Code:
# order-form.html
{% load catname %}
<form id = "order-form">
{% for form in forms %}
<div id="gun-{{ forloop.counter }}">
{% for field in form.fields %}
<div id="{{ field }}-item" class="item">
<h3>{{ field|catname }}</h3>
{% for choice in form.field.choices %} {# <-- Help me out here #}
{{ choice.id }}
{{ choice.title }}
{% endfor %}
</div>
{% endfor %}
{% endfor %}
<button type="submit" id="standard-gun-form-submit">Continue to next step</button>
</form>
# views.py
def get_form(request):
if request.method == 'POST':
if request.POST['gun_type'] == 'standard':
forms = [StandardGunForm(prefix=p) for p in range(0,2)]
return render_to_response('main/order-form.html', {'forms' : forms,}, RequestContext(request))
# forms.py
class StandardGunForm(ModelForm):
def __init__(self, *args, **kwargs):
super(StandardGunForm, self).__init__(*args, **kwargs)
for field in self.fields:
if isinstance(self.fields[field], ModelChoiceField):
self.fields[field].empty_label = None
class Meta:
model = BaseGun
widgets = {
'FrameTuning' : RadioSelect(),
'FrameConnection' : RadioSelect(),
}
exclude = ('price')
Endgame: markup that looks like this
<form id="foo">
<div class="category">
<div class="item">
<input type="radio" name="srsbzns" value="1">Option 1</input>
<img src="http://placekitten.com/150/150">
<p>Other foo here</p>
</div>
<div class="item">
<input type="radio" name="srsbzns" value="2">Option 2</input>
<img src="http://placekitten.com/150/150">
<p>Other foo here</p>
</div>
<div class="item">
<input type="radio" name="srsbzns" value="3">Option 3</input>
<img src="http://placekitten.com/150/150">
<p>Other foo here</p>
</div>
</div>
</form>
From the shell, this returns what I want
>>> forms = [StandardGunForm(prefix=p) for p in range(0,2)]\
>>> forms[0].fields['frame_tuning'].choices.queryset
I'm surprised this is proving so challenging!
Bonus: I have DEBUG = True and Django Debug toolbar enabled. Is it possible to dump the variables to the browser, so I can see what this stuff looks like as I drill down?
Thanks!
I had to do something similar and started down this path as well. I wanted to create table rows from a ModelChoiceField where each column had a different field of the model instance (and then I'd allow filtering the table rows via JavaScript).
I couldn't find it in the Django docs, but a quick perusal of the Django source showed the way. You can get to the queryset to access the model instances like so:
<form action="{% url 'some_view' %}" method="post">
{% csrf_token %}
{% if form.non_field_errors %}
{{ form.non_field_errors }}
{% endif %}
{% for field in form %}
{{ field.label }}
{% if field.field.choices %}
{% for model_instance in field.field.choices.queryset %}
{{ model_instance.id }}
{% endfor %}
{% else %}
{{ field }}
{% endif %}
{% if field.errors %}
{{ field.errors|striptags }}
{% endif %}
{% endfor %}
<button type="submit">Submit</button>
</form>
However, at this point we've disassembled the shiny widget (in my case a CheckboxSelectMultiple) and must re-assemble the HTML form input using template code. I found no direct way to simultaneously iterate over the ModelChoiceField to access the model instance fields and get the HTML form tags for the choices.
Maybe there's a way, but I abandoned my attempt and built my own HTML form, handling all the POST data in a view. It ended up much easier that way. ModelForms are really nice and convenient, but using them for something they weren't built for can end up being more difficult.
I figured I'd post this in case anyone is trying to do it for some other reason. Hope it helps.
Very late, but I'm reading now and this is what it worked for me
{% for field in form %}
{% for x, y in field.field.choices %}
{{x}}
{{y}}
{% endfor %}
{% endfor %}
Where "x" is the id or code, and "y" is the readable value or title.
You can access the underlying model instance for each choice:
{% for choice, label in form.field_name.field.choices %}
{{ choice.value }}
{{ choice.instance }}
{{ choice.instance.instance_attribute }}
{{ label }}
{% endfor %}
{% for choice in form.field.choices %} {# <-- Help me out here #}
{{ choice.id }}
{{ choice.title }}
{% endfor %}
Look what you're doing here, you're literally trying to access a field called "field" every time in this loop, which presumably does not exist.
You need to take the field object you're iterating through, and access the choices attribute on that.
{% for field in form.fields %}
{% for choice in field.choices %}