- Une première application web simple, avec Flask
- Passer de l’information dans l’URL
- Vers des vues structurées avec les templates
- Envoyer du HTML à l’utilisateur
- Exercice
Avant de démarrer cette session, assurez-vous de bien avoir un environnement de développement Flask fonctionnel. Si ce n’est pas le cas, veuillez suivre les instructions de la session 1 avant de poursuivre avec les instructions ci-dessous.
Une première application web simple, avec Flask
Nous allons voir dans cette Section comment afficher un message dans le navigateur de l’utilisateur.
Tout d’abord, créer un nouveau projet Flask qui aura pour nom “FlaskTP1” en suivant les instructions de la session précédente.
Nous allons maintenant coder une vue qui affichera le texte “Helloworld”. Pour cela, éditez le fichier app.py
généré par PyCharm de manière à ce qu’il contienne le code suivant:
@app.route('/greetings')
def some_function():
return "Helloworld!"
Lancer l’application Flask, et accéder à cette vue via l’URL http://127.0.0.1:5000/greetings:
Nous pouvons faire les observations suivantes:
- L’URL à entrer dans le navigateur est définie par la valeur passée à
@app.route
- Le résultat de l’exécution de la fonction est envoyé au navigateur
- Le nom donné à la fonction Python n’a pas d’importance
Passer de l’information dans l’URL
Il est possible de récupérer des informations contenues dans l’URL que l’utilisateur accède. Le code suivant définit une fonction qui prend 3 paramètres, et affiche un calcul:
@app.route("/sum/<lang>/<int:a>/<int:b>")
def compute_sum(lang, a, b):
c = a + b
if lang == "fr":
return "La somme de %d et %d est %d" % (a, b, c)
else:
return "The sum of %d and %d is %d" % (a, b, c)
Quand on accède à l’URL http://127.0.0.1:5000/sum/en/3/5, on obtient le résultat suivant:
Les différents types acceptés nativement dans Flask sont trouvables ici.
Vers des vues structurées avec les templates
Dans cette section nous verrons comment construire une réponse structurée. Dans un premier temps nous retournerons un texte généré en Python et transmis à Flask. Dans un second temps, nous verrons comment écrire la même chose en utilisant le moteur de template Jinja2.
Une vue construite naïvement en Python
Prenons l’exemple suivant:
import flask # mettre cette ligne en debut de fichier (surtout si vous gardez le fichier généré par Pycharm)
app = flask.Flask(__name__)
@app.route("/complex_view")
def complex_view():
# variables
colors = ["red", "blue", "green", "purple", "dark", "white"]
boolean_value = False
msg_if_boolean_value = "[boolean_value is true]"
msg_if_not_boolean_value = "[boolean_value is false]"
# construction de la reponse
result = ""
for color in colors:
result += "* "
result += color
result += "\n"
if boolean_value:
result += msg_if_boolean_value
else:
result += msg_if_not_boolean_value
# envoie de la reponse sous forme textuelle
return flask.Response(result,
mimetype="text")
En visitant l’URL http://127.0.0.1:5000/complex_view, vous obtiendez ce resultat:
La fonction complex_view
retourne une réponse textuelle construite avec deux structures de contrôle : un for
et un if
. Bien qu’étant fonctionnelle, cette fonction deviendrait difficilement lisible si le texte à ajouter devant une couleur était plus long.
De plus, en procédant de la sorte, le code de la fonction Python contient la logique de la présentation, ce qui est en contradiction avec la séparation MVC.
La même vue construite avec une template Jinja
L’objectif sera maintenant de coder l’exemple précédent, en s’appuyant sur des templates Jinja2 en prenant en compte des variables Python la génération de la réponse. Nous verrons notamment comment faire une boucle avec l’instruction for
et comment faire une structure conditionnelle avec l’instruction if/else
Pour cela, nous allons:
- 1) créer une nouvelle fonction
complex_view_template
qui contient le code suivant:
@app.route("/complex_view_template")
def complex_view_template():
colors = ["red", "blue", "green", "purple", "dark", "white"]
boolean_value = False
msg_if_boolean_value = "[boolean_value is true]"
msg_if_not_boolean_value = "[boolean_value is false]"
# Bien penser a mettre 'import flask' en debut de fichier
return flask.Response(flask.render_template("complex_view.jinja2",
colors=colors,
boolean_value=boolean_value,
msg_if_boolean_value=msg_if_boolean_value,
msg_if_not_boolean_value=msg_if_not_boolean_value),
mimetype="text")
- 2) Créer un fichier
templates/complex_view.jinja2
de manière à ce qu’elle affiche les variables passées à la fonctionrender_template
:
{% for color in colors %}
* {{ color }}
{% endfor %}
{% if boolean_value_arg %}
{{ msg_if_boolean_value }}
{% else %}
{{ msg_if_not_boolean_value }}
{% endif %}
En visitant la page http://127.0.0.1:5000/complex_view_template, vous obtiendez un résultat proche de:
On peut faire les observations suivantes:
- les valeurs des variables Python sont bien prises en compte pour former la réponse HTML : la réponse HTML sera adaptée au contenu des variables Python.
- De plus des structures de contrôle telles que les
if/else
et lesfor/while
permettent de structure le code Jinja2. - Des espaces sont ajoutés entre les lignes
(Bonus)
Bien que les espaces en trop ne posant pas de soucis lors de la génération de code HTML, il pourrait en être autrement lors de la génération d’une réponse qui serait sensible aux espaces. Dans un tel cas, il est possible d’avoir un plus grand contrôle sur la gestion des espaces (c.f. ce lien).
Le code de template suivant permet d’avoir un résultat identique à celui de la fonction en python “pur”:
{% for color in colors -%}
* {{ color }}
{% endfor %}
{%- if boolean_value -%}
{{ msg_if_boolean_value }}
{%- else -%}
{{ msg_if_not_boolean_value }}
{% endif %}
Envoyer du HTML à l’utilisateur
Dans cette section nous allons voir comment renvoyer une réponse HTML à l’utilisateur en utilisant les templates Jinja.
Tout d’abord, récupérer une archive de code session_4_engineer_view.zip contenant un projet qui servira de base pour cet exercice.
Ce TP contient une base de données pour stocker les données. Entre deux requêtes, les variables locales à une fonction python ne sont pas partagées et les variables globales seront perdues quand le serveur Flask est redémarré. Dans le cas présent, la base de données est configurée pour s’initialiser toute seule.
Les bases de données seront introduites dans une future session.
Le projet contient aussi une classe Engineer
persistante trouvable dans database/models.py
:
class Engineer(db.Model):
id = <int>
username = <string>
email = <string>
site = <string>
def __repr__(self):
return '<Engineer {}>'.format(self.username)
Cette classe hérite de db.Model
, ce qui permet de stocker les instances de cette classe dans la base de données évoquée précédemment. Au démarrage du serveur Flask, les ingénieurs précédemment ajoutés seront présents.
Ce projet contient aussi un fichier app.py qui s’occupe d’initialiser la base de données et qui propose:
- une fonction
get_all_engineers
qui retourne une liste d’ingénieurs - une fonction
get_engineer_by_id
qui retourne un ingénieur en fonction de son identifiant - une fonction
get_engineers_in_site
qui retourne les ingénieurs présents sur un site passé en paramètre - une vue
index
affichant les ingénieurs présents dans la base de données - une vue
show_engineer
qu’il faudra coder dans cet exercice
Nous allons maintenant coder une vue qui affichera la description d’un ingénieur en prenant en entrée son identifiant. Pour cela, nous allons:
- 1) créer un fichier
show_engineer.html.jinja2
dans le dossier templates (en violet dans PyCharm) - 2) y mettre le contenu suivant:
<html>
<head>
<title>Description de {{ engineer.username }}</title>
</head>
<body>
<p>Information about "{{ engineer.username }}"</p>
<dl>
<dt>id</dt>
<dd>{{ engineer.id }}</dd>
<dt>username</dt>
<dd>{{ engineer.username }}</dd>
<dt>email</dt>
<dd>{{ engineer.email }}</dd>
<dt>site</dt>
<dd>{{ engineer.site }}</dd>
</dl>
</body>
</html>
- 3) créer une nouvelle fonction python dans le fichier
app.py
, qui reprendrait le code suivant:
@app.route('/engineer/id/<int:engineer_id>')
def show_engineer_by_id(engineer_id):
engineer = get_engineer_by_id(engineer_id)
# Bien penser a mettre 'import flask' en debut de fichier
return flask.render_template("show_engineer.html.jinja2",
engineer=engineer)
En visitant la page http://127.0.0.1:5000/engineer/id/1, le résultat suivant devrait être affiché:
Exercice
Pour la prochaine séance, nous vous demandons de codez une vue qui:
- prendra en paramètre le nom d’un site (brest, nantes, …)
- affichera les ingénieurs affiliés à ce site
Pour cela vous pourrez:
- utiliser la fonction
get_engineers_in_site
qui a un site retourne les ingénieurs qui y sont affiliés - créer une template jinja
show_engineers_by_site.html.jinja2
- vous inspirer des templates vues en cours en utilisant un
for
et{{ engineer.X }}
- Suggestion : vous pouvez afficher les ingénieurs sous forme de liste HTML en utilisant la structure :
<ul>
<li>ingenieur A</li>
<li>ingenieur B</li>
<li>ingenieur C</li>
</ul>