Outils pour utilisateurs

Outils du site


dev:python:flask:flask_formulaires_wtforms

:TODO_DOCUPDATE:

Flask : Créer des formulaires via Flask-WTF

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.

Initialiser l'application 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 :

__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

Création du formulaire

  • Pour créer un formulaire, on définit une classe héritant de FlaskForm.
  • Chaque attribut de la notre classe représentant un champ de formulaire sera un objet héritant de la classe de base wtforms.fields.Field ;
  • L'objet de type formulaire est instancié et traité par le contrôleur (la fonction associée à la route Flask) et transmis à la vue (le template).
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 :

  • StringField : Champ type texte ;
  • TextAreaField :
  • SelectField :
  • BooleanField : Champ de type checkbox ;
  • PasswordField : Champ de saisie de mot de passe
  • HiddenField : Champ masqué ;
  • SubmitField : Bouton soumission du formulaire ;

Champs plus spécifiques :

  • EmailField ;
  • TelField ;
  • URLField ;
  • TimeField ;
  • ColorField ;
  • IntegerField : Saisie d'un nombre entier;
  • IntegerRangeField
  • FloatField
  • DecimalRangeField ;
  • FileField

Les filtres

Les filtres (filters) permettent de traiter, en amont de la validation, les valeurs saisies par l'utilisateur. On les utilise couramment pour :

  • Retirer les espaces en début et fin de saisie ;
  • Passer la saisie en majuscule, en minuscule ;
  • Arrondir les valeurs décimales etc.
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 validateurs

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 :

  • Le champs ne doit pas être vide,
  • La saisie doit respecter le formalisme d'un email.

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.

Rendu du formulaire dans le template

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 :

_helpers.html
{#
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>

Validation

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)
L'exemple montre bien qu'il n'est pas nécessaire de passer l'objet 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.

Débogages

Échec systématique de la validation

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>

Références

dev/python/flask/flask_formulaires_wtforms.txt · Dernière modification : 2026/04/12 10:26 de yoann