06 Formulaire
Nous avons vu comment créer notre schéma de base de données et des entités de manière automatique. Toutefois toute application web a à un moment ou un autre besoin d'utiliser des formulaires. Nous allons donc découvrir dans ce tp comment créer et gérer des saisies utilisateurs.
Flask et les formulaires
Flask ne fournit pas de librairie de gestion de formulaire par défault, il laisse la faculté à l'utilisateur de choisir la librairie qui lui convient.
Découverte de WTForms
WTForms est la librairie de gestion de formulaire. Celle-ci est open source et disponible à cette adresse et pour une rapide introduction.
Installation
Nous allons utiliser Flask WTForms
qui est une simple intégration de WTForms au sein de Flask.
Comme toujours un simple pipenv install flask-wtf
.
Notre premier formulaire
Nous allons réutiliser la base de données que nous avons créé dans le tp précédent pour permettre l'ajout et l'édition de formulaire.
Nous définissons dans un premier temps notre formulaire au sein du fichier forms.py
:
from flask_wtf import FlaskForm
from wtforms import StringField, SelectField
from wtforms.fields.html5 import DateField
from wtforms.validators import DataRequired, Length
class BoardgameForm(FlaskForm):
pass
Une fois cela fait nous pouvons définir les champs un à un :
class BoardgameForm(FlaskForm):
name = StringField('Name', validators=[
DataRequired(), Length(min=3, max=20)])
Ainsi nous indiquons que le champ name:
- a pour label
Name
; - doit être renseigné et avoir une longueur comprise entre 3 et 20 caractères (via les validators).
class BoardgameForm(FlaskForm):
name = StringField('Name', validators=[
DataRequired(), Length(min=3, max=20)])
release_date = DateField('Release date', validators=[DataRequired()])
Nous définissons également notre champ date de sortie puis enfin le champ model qui lui est un peu plus complexe:
class BoardgameForm(FlaskForm):
name = StringField('Name', validators=[
DataRequired(), Length(min=3, max=20)])
release_date = DateField('Release date', validators=[DataRequired()])
category = SelectField('Category')
Il s'agit d'un champ de type Select qui possédent comme choix les couples id/version issus de la table Category
.
Nous avons défini notre formulaire nous pouvons désormais passé à notre vue.
Vue
Dans notre fichier app.py nous allons créer la vue suivante:
@app.route('/boardgame/create')
def boardgame_create():
form = BoardgameForm()
form.category.choices = [(m.id, m.name) for m in Category.select()]
return render_template('boardgame/form.html', form=form)
Et nous allons également créer le template correspondant.
Template jinja
Créer le fichier boardgames/form.html dans le répertoire template avec le contenu suivant:
{% extends 'base.html' %}
{% block body %}
<h2>Create a boardgame</h2>
<form method="POST">
{% for field in form %}
{% if field.widget.input_type != 'hidden' %}
{{ field.label() }}
{% if field.errors %}
{% for error in field.errors %}
<div class="alert alert-error">{{ error }}</div>
{% endfor %}
{% endif %}
{% endif %}
{{ field() }}
{% endfor %}
<button type="submit">Save</button>
</form>
{% endblock %}
Nous affichons un à un les champs de notre formulaire en n'affichant que si nécessaire les labels. Les messages d'erreurs par champs sont également gérés.
Essayez maintenant de soumettre votre formulaire que se passe t'il ?
Méthodes HTTP
Pour informer Flask qu'il doit accepter d'autres méthodes HTTP que GET
il suffit de l'indiquer dans la signature de la route.
@app.route('/boardgame/create', methods=['GET', 'POST', ])
def boardgame_create():
pass
Vous aurez besoin au préalable d'indiquer une clé secrête à votre application Flask pour que celle-ci puisse gérer la validation csrf.
Par exemple:
app.secret_key = 'HelloWorld' #Don't use it .. !
Essayez maintenant de soumettre votre formulaire que se passe t'il ?
Gestion du formulaire
La méthode validate_on_submit
permet de gérer automatiquement la récupération des données depuis le corps de la requête POST et de valider celles-ci.
@app.route('/boardgame/create', methods=['GET', 'POST', ])
def boardgame_create():
form = BoardgameForm()
if form.validate_on_submit():
pass
return render_template('boardgame/form.html', form=form)
Essayez maintenant de soumettre votre formulaire que se passe t'il ?
Gestion des données en base
Nous allons créer une instance de boardgame (vide) qui sera ensuite peupler depuis notre formulaire via la méthode populate_obj
@app.route('/boardgame/create', methods=['GET', 'POST', ])
def boardgame_create():
boardgame = Boardgame()
form = BoardgameForm()
if form.validate_on_submit():
form.populate_obj(boardgame)
boardgame.save()
flash('Hooray ! Boardgame created !')
return redirect(url_for('boardgames'))
return render_template('boardgames/form.html', form=form)
La fonction flash
permet de lancer des messages .. flash au sein de notre application. Pour en savoir plus je vous invite à lire encore une fois la documentation.
Vous aurez besoin de mettre à jour votre fichier jinja (base.html ?) avec le contenu suivant:
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
Essayez maintenant de soumettre votre formulaire que se passe t'il ?
Type de données
Les données reçues depuis le corps d'une requête POST sont toutes de types string
. Il convient donc d'indiquer à notre SelectField
que le type de données reçu doit être forcer en int
.
class BoardgameForm(FlaskForm):
name = StringField('Name', validators=[
DataRequired(), Length(min=3, max=20)])
release_date = DateField('Release date', validators=[DataRequired()])
category = SelectField('Category', coerce=int)
Définir un formulaire plus simplement
Il existe une librairie qui permet de faire le lien entre un model peewee et un formulaire wtf, celle-ci se nomme wtf-peewee
. Vous savez désormais comment l'installer dans votre environnement virtuel.
Créer la classe formulaire automatiquement
Au sein de notre fichier forms.py
:
from wtfpeewee.orm import model_form
SimpleBoardgameForm = model_form(Boardgame)
Utilisation de cette classe
Voici un exemple de vue pour l'édition d'un boardgame:
@app.route('/baordgame/edit/<int:boardgame_id>/', methods=['GET', 'POST'])
def boardgame_edit(boardgame_id):
try:
boardgame = Boardgame.get(id=boardgame_id)
except Entry.DoesNotExist:
abort(404)
if request.method == 'POST':
form = SimpleBoardgameForm(request.form, obj=boardgame)
if form.validate():
form.populate_obj(boardgame)
boardgame.save()
flash('Your entry has been saved')
else:
form = SimpleBoardgameForm(obj=boardgame)
return render_template('boardgames/form.html', form=form, boardgame=boardgame)
À vous de jouer
- Créer une route éditer un jeu;
- Créer une route pour ajouter/modifier une catégorie;
- Créer une route pour supprimer un jeu.
Conclusion
Nous venons de voir que gérer les formulaires et leurs validations est extrèmement simple avec les bons outils.