Pass Bootstrap HTML attributes to Flask-WTForms

Flask-WTForms helps us create and use web forms with simple Python models. WTForms takes care of the tedious, boring and necessary security required when we want to use data submitted to our web app via a user on the Internet. WTForms makes data validation and Cross Sight Forgery Request (CSFR) avoidane a breeze. Out of the box, however, WTForms creates ugly forms with ugly validation. Flask-Bootstrap provides a professional layer of polish to our forms, with shading, highlights and pop ups.

Flask-Bootstrap also provides a “quick_form” method, which commands Jinja2 to render an entire web page based on our form model with one line of code.

In the real world, unfortunately, customers have strong opinions about their web pages, and may ask you to tweak the default appearance that “quick_form” generates. This blog post shows you how to do that.

In this blog post you will:

  • Deploy a web app with a working form, to include validation and polish
  • Tweak the appearance of the web page using a Flask-WTF macro
  • Tweak the appearance of the web page using a Flask-Bootstrap method

The Baseline App

The following code shows the baselined application, which uses “quick_form” to render the form’s web page. Keep in mind that this application doesn’t do anything, although you can easily extend it to persist data using an ORM (for example). I based the web app on the following Architecture:



The web app contains (contains form model), take_quiz_template.html (renders the web page) and (the web app that can route to functions based on URL and parse the form data).

[ec2-user@ip-192-168-10-134 ~]$ tree flask_bootstrap/
├── requirements.txt
└── templates
    └── take_quiz_template.html

1 directory, 4 files
[ec2-user@ip-192-168-10-134 ~]$ 

Install the three files into your directory. As seen in the tree picture above, be sure to create a directory named templates for take_quiz_template.html.

Create and activate your virtual environment and then install the required libraries.

[ec2-user@ip-192-168-10-134 ~]$ virtualenv flask_bootstrap/
New python executable in flask_bootstrap/bin/python2.7
Also creating executable in flask_bootstrap/bin/python
Installing setuptools, pip...done.
[ec2-user@ip-192-168-10-134 ~]$ . flask_bootstrap/bin/activate
(flask_bootstrap)[ec2-user@ip-192-168-10-134 ~]$ pip install -r flask_bootstrap/requirements.txt


Successfully installed Flask-0.11.1 Flask-Bootstrap- Flask-WTF-0.13.1 Jinja2-2.8 MarkupSafe-0.23 WTForms-2.1 Werkzeug-0.11.11 click-6.6 dominate-2.3.1 itsdangerous-0.24 visitor-0.1.3
(flask_bootstrap)[ec2-user@ip-192-168-10-134 ~]$ 

Start your flask application and then navigate to your IP address. Since this is just a dev application, you will need to access port 5000.

(flask_bootstrap)[ec2-user@ip-192-168-10-134 ~]$ cd flask_bootstrap/
(flask_bootstrap)[ec2-user@ip-192-168-10-134 flask_bootstrap]$ ./ 
 * Running on (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger pin code: 417-431-486

This application uses the quick_form method to generate a web page. Note that the application includes all sorts of goodies, such as CSFR avoidance, professional looking highlights and validation. Play around with the page to look at the different validation pop-ups and warnings.

Now imagine that your customer wants to change the look of the submit button, or add some default text. In this situation, the quick_form does not suffice.

Attempt 1: Use a Flask-WTF Macro

The Flask-WTF docs include a Macro named render_field which allows us to pass HTML attributes to Jinja2. We save this macro in a file named _formhelpers.html and stick it in the same templates folder as take_quiz_template.html.

{% macro render_field(field) %}
  <dt>{{ field.label }}
  <dd>{{ field(**kwargs)|safe }}
  {% if field.errors %}
    <ul class=errors>
    {% for error in field.errors %}
      <li>{{ error }}</li>
    {% endfor %}
  {% endif %}
{% endmacro %}

Now, update the take_quiz_template.html template to use the new macro. Note that we lose the quick_form shortcut and need to spell out each form field.

When you go to your web page you will see the default text we added to the field:

{{ render_field(form.essay_question, class='form-control', placeholder='Write down your thoughts here...') }}

And an orange submit button that spans the width of the page:

{{ render_field(form.submit, class='btn btn-warning btn-block') }}

You can see both of these changes on the web page:

Unfortunately, if you click submit without entering any text, you will notice that we have reverted to ugly validations.

Attempt 2: Use Flask-Bootstrap

Although pretty much hidden in the Flask-Bootstrap documents, it turns out you can add extra HTML elements directly to the template engine using form_field.

As before, we add default text with “placeholder:”

{{ wtf.form_field(form.essay_question, class='form-control', placeholder='Write down your thoughts here...') }}
{{ wtf.form_field(form.email_addr, class='form-control', placeholder='') }}

We then customize the submit button. You can customize the button however you would like. Take a look here for more ideas.

{{ wtf.form_field(form.submit, class='btn btn-warning btn-block') }}

This gives us a bootstrap rendered page with pretty validation:

As you can see, we get a popup if we attempt to submit without entering text.


You now have a working web application that easily renders professional looking forms with validation and pop-ups. In the future you can trade ease of deployment against customability.

Published by

John Sobanski

John, a Senior Cloud Architect at Solers Inc. enjoys Big Data Infrastructure, Business Development, Machine Learning, Software Defined Networks and coding for charity.