{{tag>dev python flask web formulaire}} :TODO_DOCUPDATE: ====== Flask : Créer des formulaires via Flask-WTF ====== [[https://wtforms.readthedocs.io/en/3.2.x/|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'' : #!/usr/bin/env python from flask import Flask app = Flask(__name__) @app.route('/') def show_home_page(): """Build and return home page """ return '

Hello World!!!

' 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!!!" {{capture-2025-02-27_01.png}} 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. * Ici la [[https://wtforms.readthedocs.io/en/3.2.x/validators/|documentation exhaustive des validators WTForms]] ===== 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 : {# Produire le code HTML d'un champ de formulaire. #} {% macro render_field_with_div(field) %}
{{ field.label }} {{ field(**kwargs)|safe }} {% if field.errors %} {% endif %}
{% endmacro %}
Une fois la macro définie, on peut l'utiliser dans les différents modèles produisant des formulaires.
{% from "_helpers.html" import render_field_with_div %}
{{ 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) }}
===== 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.csrf_token }}
===== Références ===== * [[https://flask-wtf.readthedocs.io/en/1.2.x/|Documentation Flask-WTF]] * [[https://flask.palletsprojects.com/en/stable/patterns/wtforms/| Documentation Flask : validation des formulaires WTF (palletsprojects.com) (en)]] * [[https://wtforms.readthedocs.io/en/3.2.x/|Documentation Officielle WTForms]] * [[https://spectre-org.github.io/spectre-docs/docs/get-started/|Documentation Framework CSS Spectre]] * https://zephyrnet.com/fr/manipulation-de-formulaires-en-flacon-avec-flacon-wtforms/ * https://fr.python-3.com/?p=1882 * Vidéo [[https://youtu.be/HvEkVffiNXQ| Créer des formulaires avec Flask-WTF]] * https://picturepan2.github.io/spectre/getting-started.html * https://spectre-org.github.io/spectre-docs/docs/layout/ * [[https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-iii-web-forms| Les formulaires dans Flask (miguelgrinberg.com)]]