==================== Le framework Flask ==================== .. role:: en .. role:: lat .. highlight:: python .. |framework| replace:: `framework`:en: .. |template| replace:: `template`:en: Premiers pas en Flask ===================== Hello world ----------- :: 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 par un serveur compatible. * La fonction ``greeting`` 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 ``greeting`` est un `décorateur <318>`:pep: python. Il sert à indiquer l'URL pour laquelle cette vue doit être utilisée. .. _serveur-dev: Serveur de développement ------------------------ * Il n'est pas forcément aisé de mettre en place un serveur Web pour tester notre application. * Flask fournit son propre *serveur de développement*, directement dans la méthode ``run`` de l'application. On peut donc créer un script ``serveur.py`` contenant simplement : .. code-block:: python # en supposant que votre application Flask est définie # dans un fichier nommé 'mon_projet.py' from mon_projet import app app.run(debug=True) .. nextslide:: * Le mode ``debug`` offre de plus 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. 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`:lat: 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/") def hello(name): return "Hello %s" % name .. note:: Il est possible d'avoir plusieurs paramètres, par exemple ``/hello//``. Il est également possible d'imposer un type aux paramètres, par exemple ``/user/``. `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" .. nextslide:: * on peut passer à ``url_for`` des paramètres supplémentaires (`i.e.`:lat: 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/ .. nextslide:: Exemple d'utilisation :: @app.route("/about") def about(): return """Retour à la page d'accueil""" % \ url_for('root') .. hint:: ``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`:lat: 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. https://flask.palletsprojects.com/en/1.1.x/api/?highlight=request#flask.Request `Templates`:en: 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 : .. code-block:: html+jinja

Hello {{name}}

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

Hello world

`Templates`:en: en Flask ------------------------ * Flask utilise le système de `templates`:en: `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/") def hello(n): return render_template("hello.html", name=n) .. _Jinja2: http://jinja.pocoo.org/ 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`:en:, 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 ``{% %}``. .. code-block:: html+jinja {% if elements %} {% else %}

Aucun élément

{% endif %} Héritage -------- ``layout.html``: .. code-block:: html+jinja Le titre du site

Le titre du site

{% block content %}{% endblock %}
©2017 Université Lyon 1
``hello.html``: .. code-block:: html+jinja {% extends "layout.html" %} {% block content %}

Hello {{name}}

{% 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") .. hint:: 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 personnaliser 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``. .. _WSGI: https://www.python.org/dev/peps/pep-3333/