WTForms est une bibliothèque Python permettant de gérer des formulaires web via des Classes et objets en POO.
Ici on utilise le paquet Flask-WTF qui intègre la bibliothèque WTForms au micro framework Flask.
Création d'un dépôt git pour le projet et initialisation de l'application Flask :
git init hello-wtf cd hello-wtf/ git branch -m main # Création de l'environnement et installation des packages python3 -m venv venv source venv/bin/activate pip install Flask Flask-WTF email_validator # Création de l'application Flask mkdir -p helloforms/{static,templates} touch helloforms/__init__.py chmod a+x helloforms/__init__.py
Peupler le fichier helloforms/init.py :
#!/usr/bin/env python from flask import Flask app = Flask(__name__) @app.route('/') def show_home_page(): """Build and return home page """ return '<h1>Hello World!!!</h1>' if __name__ == '__main__': # execution en mode debug app.run(debug=True)
Tester l'application via la commande flask run:
FLASK_APP=helloforms FLASK_ENV=development flask run --debug
A ce stade la commande doit lancer le serveur web et afficher un message du type :
* Serving Flask app 'helloforms' * Debug mode: on WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on http://127.0.0.1:5000 Press CTRL+C to quit * Restarting with stat * Debugger is active! * Debugger PIN: 845-679-934
Ouvrir son navigateur à l'URL http://localhost:5000 doit afficher le message “Hello World!!!”
Pour la mise en forme, on utilise le framework Spectre CSS
wtforms.fields.Field ;from flask_wtf import FlaskForm from wtforms import EmailField, SubmitField from wtforms.fields.simple import BooleanField class NewsLetterForm(FlaskForm): email = EmailField(label='Email') news_letter = BooleanField(label="S'abonner à la news letter") submit_button = SubmitField(label='Valider')
Comme illustré ci-dessus, le constructeur des objets héritant de la classe de base wtforms.fields.Field accepte notamment les paramètres :
label : le label associé au champ ;default : valeur par défaut du champ, peut être un callable (un nom de fonction exécutable) ;description : une description associée au champ, utilisée généralement par les help text ;filters : tableau, séquence de callable exécutés pour traiter les données du champ ;validators : tableau de validators appelés lors de l'exécution de validate.La liste exhaustive des paramètres est disponible dans la documentation https://wtforms.readthedocs.io/en/3.2.x/fields/#basic-fields
Quelques objets représentant les champs de base des formulaires :
Champs plus spécifiques :
Les filtres (filters) permettent de traiter, en amont de la validation, les valeurs saisies par l'utilisateur. On les utilise couramment pour :
from flask_wtf import FlaskForm from wtforms import StringField class RegisterForm(FlaskForm): lastname = StringField(label='Nom', default="DOE", filters=[str.strip, str.upper], description="Champ de saisie de votre nom")
Ci-dessus, on utilise les filtres pour retirer les espaces autour de la saisie et la passer en majuscules.
Les validators permettent de contrôler la saisie de l'utilisateur. On les importe depuis wtforms.validators.
from flask_wtf import FlaskForm from wtforms import StringField from wtforms.validators import InputRequired, Email class RegisterForm(FlaskForm): email = StringField(label='Email', validators=[InputRequired(),Email()])
Ici on instancie deux validators pour contraindre la saisie de l'utilisateur :
Dans ce cas, peut également utiliser la classe EmailField :
from flask_wtf import FlaskForm from wtforms import EmailField class RegisterForm(FlaskForm): email = EmailField(label='Email')
Il existe de nombreux validators citons par exemple InputRequired, Length, Email, URL, NumberRange, IPAddress, MacAddress, Regexp, etc. Il est possible de définir ses propres validators.
Du côté du template, on peut s'appuyer sur des macros Jinja2 pour factoriser le code en charge de produire le HTML du formulaire :
{#
Produire le code HTML d'un champ de formulaire.
#}
{% macro render_field_with_div(field) %}
<div>
{{ field.label }} {{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endmacro %}
Une fois la macro définie, on peut l'utiliser dans les différents modèles produisant des formulaires.
<div> {% from "_helpers.html" import render_field_with_div %} <form method="POST" action="{{ url_for('frontend.parse_contact_form') }}"> {{ form.csrf_token }} {{ render_field_with_div(form.first_name) }} {{ render_field_with_div(form.last_name) }} {{ render_field_with_div(form.email) }} {{ render_field_with_div(form.subject) }} {{ render_field_with_div(form.message) }} {{ render_field_with_div(form.submit_button) }} </form> </div>
C'est la fonction contrôleur de la vue qui se charge de valider le formulaire retourné en méthode POST.
@frontend_bp.post('/contact') def parse_contact_form(): """ Analyse le formulaire de contact renseigné par l'utilisateur. """ from app.services.forms import ContactForm form = ContactForm() if form.validate_on_submit(): # Les valeurs saisies sont correctes return redirect(url_for('frontend.supply_home')) else: # Reafficher le formulaire pour permettre à l'utilisateur de corriger flash('Vérifier les valeurs saisies') return render_template('contact.html', form=form)
request.form à l'instanciation du formulaire ContactForm (héritant de FlaskForm) : il sera automatiquement chargé.
La méthode validate_on_submit() vérifie que la méthode POST est utilisée et que le la saisie est valide.
Lors du traitement du formulaire dans la vue, celui-ci est systématiquement considéré comme invalide.
Lorsqu'un formulaire hérite de FlaskForm, il contient systématiquement un jeton CSRF (processus de sécurité). Celui-ci doit être rendu dans le template.
<form method="POST" action="/"> {{ form.csrf_token }} <!-- vos autres champs ici --> <input type="submit" value="Go"> </form>