Flask

El objetivo no es enseñarte Flask, ni WTForms, ni Bootstrap. Sino juntar estas tres tecnologías que ya conoces para realizar un formulario vistoso. En el siguiente ejemplo te mostraré como hacer una validación de un campo con WTForms y mostrar su mensaje de error con los estilos de Bootstrap.

Necesito tres archivos básicos: un HTML, un Python con la estructura mínima de Flask y donde irá nuestro formulario de WTForms.

HTML

Dentro de templates creamos un archivo llamado formulario.html. Contendrá el siguiente código.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
		<title>Enemigos</title>
	</head>
	<body>
		<div class="container">
			<form action="" method="post">
				{{ form.csrf_token }}
				<div class="form-group{%if form.nombre.errors %} has-error{% endif %}">
					{{ form.nombre.label }} 
					{{ form.nombre(class='form-control', value='') }}
					{% for error in form.nombre.errors %}
						<span class="help-block">{{ error }}</span>
					{% endfor %}
				</div>
				<input type="submit" class="btn btn-success" value="Añadir">				
			</form>
		</div>
	</body>
</html>

Nos aseguramos que en static tenemos css/bootstrap.min.css.

Flask

En un archivo llamado app.py, copiamos lo siguiente.

from flask import Flask, render_template, request
from forms import EnemigosForm
app = Flask(__name__)
app.secret_key = 'secreto'

@app.route("/", methods=('GET', 'POST'))
def index():
    form = UserForm()
    if form.validate_on_submit():
		# Tu código si pasa las validaciones
		pass
    return render_template('enemigos.html', form=form)


if __name__ == "__main__":
    app.run()

WTForms

Ahora toca el formulario con la validación campo obligatorio y el mensaje que saltará si no se cumple. Creamos forms.py. Y pegamos.

from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired

class EnemigosForm(FlaskForm):
    nombre = StringField('nombre', validators=[DataRequired('El nombre es obligatorio')])

Y se acabó.

Demo Demo

EXTRA: Autogenerar todos los campos en el HTML

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
		<title>Enemigos</title>
	</head>
	<body>
		<div class="container">
			<form action="" method="post">
				{% for campo in form %}
					{% if campo.type != 'CSRFTokenField' %}
						<div class="form-group{%if campo.errors %} has-error{% endif %}">
						{{ campo.label }} 
						{% if campo.type in ('StringField', 'PasswordField')  %}
							{{ campo(class='form-control') }}
						{% else %}
							{{ campo() }}
						{% endif %}
						{% for error in campo.errors %}
							<span class="help-block">{{ error }}</span>
						{% endfor %}
						</div>
					{% else %}
						{{ campo() }}
					{% endif %}
				{% endfor %}
				<input type="submit" class="btn btn-success" value="Añadir">				
			</form>
		</div>
	</body>
</html>