Le framework Flask§

Pourquoi un framework ?§

WSGI est minimaliste§

  • Son objectif : la compatibilité
    • entre différents serveurs Web
    • entre différents types d’application

→ plus grand dénominateur commun

Inconvénients§

  • beaucoup de code boilerplate
  • pas de fonctionalités de haut niveau

Qu’est-ce qu’un framework ?§

  • Une bibliothèque fournissant des outils génériques
  • destinés à un certain type d’applications Web,
  • et favorisant la mise en œuvre de bonnes pratiques.

Frameworks Web en Python§

Flask : premiers pas§

Hello World Flask§

from flask import Flask

app = Flask(__name__)

@app.route("/")
def root():
    return "Hello world"

Explications§

  • app est une application Flask; elle est entre-autre homogène à une fonction WSGI, donc elle peut être utilisée comme la fonction application vue au cours précédent.
  • La fonction root est appelée une vue. Elle retourne une chaîne de caractères, qui sera le contenu de la réponse. Par défaut, le statut de la réponse est 200, et le type de contenu est HTML, encode en UTF-8. On verra plus tard comment générer d’autres types de réponses.
  • La ligne qui précède la fonction root est un décorateur python. Il sert à indiquer l’URL pour laquelle cette vue doit être utilisée.

Routes§

  • En développement Web, on apelle route une URL ou un ensemble d’URLs conduisant à l’exécution d’une fonction donnée.

  • Dans Flask, les routes sont déclarées via le décorateur app.route, comme dans l’exemple ci-dessus.

  • Une route peut être paramétrée, auquel cas le paramềtre sera passé à la fonction vue :

    @app.route("/hello/<name>")
    def hello(name):
        return "Hello %s" % name
    

Note

Il est possible d’avoir plusieurs paramètres, par exemple /hello/<a>/<b>.

Il est également possible d’imposer un type aux paramètres, par exemple /user/<int:ident>.

Plus d’information dans la documentation.

Génération d’URL§

  • Les routes permettent à Flask de trouver la vue correspondant à une URL, mais également de faire l’inverse, à savoir de reconstruire l’URL d’une vue donnée.

  • La fonction flask.url_for prend en paramètre le nom d’une vue (le nom de la fonction, dans une chaîne de caractères), avec ses éventuels paramètres, et retourne l’URL correspondante. Exemples :

    # avec les routes des exemples précédents
    url_for('root')                # → "/"
    url_for('hello', name="John")  # → "/hello/John"
    
  • on peut passer à url_for des paramètres supplémentaires (i.e. non spécifiés par la vue), lesquels seront ajoutés en paramètres d’URL :

    url_for('hello', name="John", foo="bar")  # → /hello/John?foo=bar
    
  • le paramètre _external peut être mis à True pour géréner une URL absolu :

    url_for('root', _external=True)  # → http://localhost:5000/
    

Exemple d’utilisation :

@app.route("/about")
def about():
    return """<a href="%s">Retour à la page d'accueil</a>""" % \
        url_for('root')

Indice

url_for rend le code plus facile à maintenir : si on décide de changer l’URL d’une vue, il suffit de changer le paramètre de app.route, et toutes les autres vues contenant des liens vers elle s’adapteront automatiquement.

Ressources statiques§

  • Une application Web ne se limite pas aux contenus HTML générés par les vues; on a également besoin de ressources statiques (CSS, images...).

  • Dans Flask, il est possible de stocker des fichiers dans un répertoire static, situé dans le même répertoire que le fichier Python définissant l’application.

  • L’URL de ces fichiers est donnée par la fonction url_for :

    url_for("static", filename="nom_du_fichier.css")
    

Où est ma requête ?§

  • Contrairement aux fonctions WSGI, les vues Flask ne reçoivent pas directement l’information contenue dans la requête HTTP.
  • Cette information est accessible via l’objet flask.request.
  • Cet objet possède un certain nombre d’attributs, dont :
    • method généralement GET ou POST
    • headers un dictionnaire(-like) contenant les en-têtes
    • environ l’environnement WSGI sous-jacent (pour les nostalgiques ;-)

cf. http://flask.pocoo.org/docs/0.12/api/#incoming-request-data

Serveur de développement§

  • Comme indiqué ci-dessus, l’objet app est homogène à une fonction WSGI. On peut donc réutiliser le script serveur vu au cours précédent.

  • Mais Flask fournit son propre serveur de développement, directement dans la méthode run de l’application. Le script serveur devient donc :

    from mon_projet import app
    app.run(debug=True)
    
  • Le mode debug offre notamment des fonctions avancées, notamment :
    • le recharchement automatique des fichiers python en cas de modification,
    • l’affichage des exceptions dans le navigateur,
    • la possibilité d’interagir avec le code python depuis le navigateur en cas d’erreur.

Templates Jinja2§

Qu’est-ce qu’un template?§

  • Un template, ou “modèle”, est un fichier dont certaines parties seront remplacées à l’exécution.

  • Voici un exemple minimaliste :

    <p>Hello {{name}}</p>
    

    où la partie entre doubles-accolades sera remplacée par le contenu de la variable name, par exemple :

    <p>Hello world</p>
    

Templates en Flask§

  • Flask utilise le système de templates Jinja2, qui permet de générer n’importe quel format textuel (HTML, CSS...).

  • Les templates sont stockés dans un répertoire templates, situé dans le même répertoire que le fichier Python définissant l’application.

  • Pour appliquer un template, il suffit d’appeler la fonction flask.render_template en lui passant + le nom du template (relativ au répertoire templates), et + la liste des variables utilisées par le template :

    @app.route("/hello/<n>")
    def hello(n):
        return render_template("hello.html", name=n)
    

Substitution§

  • Dans une template Jinja2, les doubles-accolades {{ }} servent à indiquer une substitution.
  • Elles peuvent contenir un nom de variable, mais également des expressions plus complexes, par exemple {{reqest.method}} ou {{row[0]}}.
  • Certaines variables sont transmises automatiquement aux templates, notamment l’object requête request.

Structures de contrôle§

  • Jinja2 offre des structures de contrôles (condition, boucle) similaires à celles de Python.
  • Elles sont encadrées par les symboles {% %}.
{% if elements %}
  <ul>
  {% for e in elements %}
    <li><a href="{{e.link}}">{{e.name}}</li>
  {% endfor %}
  </ul>
{% else %}
  <p>Aucun élément</p>
{% endif %}

Héritage§

layout.html:

<!DOCTYPE html><html>
<head><title>Le titre du site</title></head>
<body>
  <h1>Le titre du site</h1>
  {% block content %}{% endblock %}
  <footer>©2017 Université Lyon 1</footer>
</body></html>

hello.html:

{% extends "layout.html" %}
{% block content %}
  <p>Hello {{name}}</p>
{% endblock %}

Note

  • La directive extends permet à un template d’hériter d’un autre.
  • La directive block permet
    • au template parent de définir des zones personalisables, et
    • au template enfant de donner un contenu aux blocs définis par son parent.

Formulaires§

Routes et méthodes§

  • Il est possible de spécifier la/les méthode(s) autorisée(s) pour une vue :

    @app.route("/user/new", methods=['GET', 'POST'])
    def user_new():
        if request.method == 'GET':
            # ...
        else:
            # ...
    

Récupération des paramètres§

  • request.args est un dictionnaire contenant les paramètres d’URL.
  • request.form est un dictionnaire contenant les données de formulaires envoyées par POST (le cas échéant).

Réponses personalisées§

Erreur§

Pour déclencher une réponse d’erreur (code HTTP 4xx), on peut utiliser la fonction flask.abort en lui passant le code de statut à retourner (et éventuellement, un message personnalisé).

Exemple :

abort(403, "Cette operation n'est pas autorisee")

Indice

La fonction abort lève une exception (ce qui explique qu’elle ne nécessite pas de return). Cette exception est interceptée par l’application Flask, qui la transforme en réponse HTTP avec le code d’erreur associé.

Redirecton§

Pour rediriger le client vers une autre URL (code HTTP 3xx), on peut utiliser la fonction flask.redirect.

Exemple :

return redirect(url_for("root"))

Pour aller plus loin§

Lorsqu’une vue retourne une chaîne de caractères (ce que nous avons vu jusqu’à maintenant), l’application Flask génère automatiquement une réponse standard (statut 200, contenu de type HTML).

Il est également possible pour une vue de retourner directement un objet Response, dont on peut alors personaliser les méta-données. Cet objet peut être produit grâce à la fonction flask.make_response.

Exemple :

resp = make_response(render_template("hello.html", name=n))
resp.headers["Cache-control"] = "max-age=3600"
return resp

Note

La fonction redirect vue ci-avant retourne un objet Response.