Python pour

le web

Définir le web

Le web c'est quoi ?

Un utilisateur

Un réseau

Un serveur

1. Fait une requête HTTP auprès du réseau pour demander une ressource sur un nom de domaine

2. Transmet la demande de ressource au serveur lié au nom de domaine

4. Achemine le résultat jusqu'à l'utilisateur

3. Traite la demande (en cherchant ou générant la ressource), et renvoie le résultat au réseau à destination de l'utilisateur

Dans ce cours.

Nous verrons

Nous ne verrons pas

Comment gérer la partie serveur pour Python

Comment gérer le traitement des requêtes en Python

Comment créer une page web Python

Le langage HTML/CSS.

Le JavaScript.

Le SQL.

Le design.

Bref, les bases du web.

Vous devez déjà savoir tout ça !

Python et le web

Pourquoi utiliser Python pour le web ?

Bonne maturité

Transposable au logiciel lourd

Bonne documentation

Comment adapter Python au web ?

Pas pensé pour le web au départ -> nécessite un framework. Beaucoup de frameworks disponibles -> il faut en choisir un.

Flask ou Django ?

Flask

Django

Type : Micro-framework

Avantages : Simple, léger, demande peu de concepts, rapide à déployer, one file.

Projets : Petits ou moyens

Popularité : Très populaire pour les petits projets

Type : Framework

Avantages : Puissant, complet, énorme écosystème, MVC & ORM, etc.

Projets : Moyens et gros

Popularité : Le framework Python le plus populaire

Petit projet

Peu de temps

Débutants pas formés

Créer notre premier projet Flask

Installer Flask

Flask n'est pas une librairie standard, vous devez donc l'installer avec pip. Pour cela, il vous suffit d’exécuter la commande ci-dessous.

sudo pip3 install flask

Créer la structure du projet Flask

Les projets Flask ont tous à peu prêt la même structure. Un dossier qui contient le projet (ici projet-flask), un dossier templates et un dossier static qui contient un dossier css, javascript et images.

Créez les différents dossiers pour avoir une arborescence comme celle-ci.

Créer le projet

Par défaut, toute application Flask tiens en un seul fichier. Créez donc le fichier app.py dans projet-flask.

Et mettez-y le code suivant.

#!/usr/bin/env python3.5
# -*- coding:utf-8 -*-

from flask import Flask

app = Flask(__name__)

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

Importe Flask pour pouvoir l'utiliser

Créer l'application Flask et la stock dans la variable app

Permet de lancer l'application Flask directement depuis le terminal en mode débug. Cette ligne doit être la dernière du fichier.

Gérer la configuration

Souvent un site web utilise des service externes (API, Base de données, etc.) qui nécessitent de stocker des clés, des constantes, des mots de passe, etc.

Pour stocker ces infos de façon maintenable on utilise des fichiers de configuration.

Nous allons créer deux fichiers de configuration différents.

Pour cela, Flask dispose de plusieurs solutions, mais le plus simple est de créer un fichier de configuration à la racine du projet.

config.py :

Pour les infos liés au fonctionnement du site (adresses, T.V.A, nom de l'appli, etc.)

Pour les infos secrètes (accès base de données, clefs API, mots de passe, etc.)

secret_config.py :

Il ne nous reste plus qu'à définir des valeurs, les rendre accessibles à l'app et les récupérer.

Définir une valeure :

Il suffit d'écrire NOM_VALEUR = 'valeur' dans le fichier de configuration.

Vous devez dire à Flask quel fichier utiliser avec les lignes ci-dessous.

Charger les valeures :

app.config.from_object('config')
app.config.from_object('secret_config')

Il vous suffit de faire app.config['NOM_VALEUR']

Récupérer une valeure :

app.config['DATABASE_PASSWORD']

Petit point particulier, un certain nombre de configurations sont directement chargées par Flask, modifient son comportement et sont accessibles en faisant app.nom_valeur.

 

La liste est dispo ici : http://flask.pocoo.org/docs/0.12/config/#builtin-configuration-values

Créer notre première route

La notion de route

Pour créer une page, Flask travail en associant une route à une fonction.

Une route, c'est l'adresse qui sert à accéder à la page web. Par exemple, sur un site example.com on pourrait accéder à la page d'accueil avec la route '/accueil'.

D'habitude, les URL utilisent l’adresse du fichier demandé et le serveur l'affiche à l'utilisateur. Mais avec les framework on utilisent plutôt des routes, plus lisibles, plus maintenables, meilleures pour le référencement.

Système classique

Système avec route

Créer notre première route

Nous allons créer la page d'accueil liée à la route "/" et à la fonction index qui retournera un titre H1 "Hello World !".

#!/usr/bin/env python3.5
# -*- coding:utf-8 -*-
from flask import Flask
app = Flask(__name__)


@app.route('/')
def index () :
    return 'Hello World !'


if __name__ == '__main__':
    app.run(debug = True)

Pour définir la route, on utilise le décorateur @app.route('la_route_voulue') au dessus de la fonction.

Le décorateur permet de déclarer la route "/" à Flask et de la liée à la fonction qui sera déclarée tout de suite après.

On définie la fonction index qui sera appelée quand l'utilisateur accédera à la route "/".

La fonction retourne le texte '<h1>Hello World !</h1>' qui sera transmis à Flask, qui renverra la chaîne à l'utilisateur.

Créer une route avec des arguments

@app.route('/show-sentence/<sentence>/')
def show_sentence (sentence) :
    return 'You say : ' + sentence

Plutôt que d'utiliser une route fixe, il est possible de créer une route avec des arguments.

Pour cela, il faut utiliser la forme <nom_argument> et ajouter nom_argument dans les paramètres de la fonction associée à la route pour pouvoir récupérer cette valeure.

La fonction ci-dessous affiche ce que l'utilisateur rentre dans l'adresse.

Formater des arguments

@app.route('/multiply/<int:first_number>/<int:second_number>/')
def multiply (first_number, second_number) :
    result = first_number * second_number
    return 'Result of {} * {} = {}'.format(first_number, second_number, result)

Par défaut, tous les arguments passés sont des string, mais il est possible de demander un type particulier. Par exemple un int.

Pour cela, il faut utiliser la forme <type:nom_argument>.

La fonction ci-dessous affiche le résultat d'une multiplication.

Petite astuce

Mettez toujours un "/" à la fin de vos routes. Par exemple, créez la route "/bonjour/" plutôt que "/bonjour".

Pourquoi ? Parce-que Flask peut rajouter un "/" manquant dans une URL, mais pas en retirer un en trop !

Créer notre première page web

Une page web c'est quoi ?

Avec Flask, on associe une route à une fonction.

Pour créer une page web, il suffit donc de demander à cette fonction d'afficher une chaîne de caractère qui soit du HTML.

Pour cela, plutôt que d'utiliser l'instruction print() de Python, on utilise return pour retourner un résultat. C'est Flask qui va automatiquement transformer ce résultat en réponse HTTP sous forme de texte affiché à l'utilisateur.

Une page web, c'est simplement une réponse HTTP qui contient un texte dans un format interprétable par le navigateur : le HTML.

Créer une page web simple

Nous allons créer un page web très simple qui affiche simplement la page ci-dessous.

Cette page sera accessible à l'adresse "/lorem-ipsum/".

Pour cela, ajouter le code ci-dessous dans l'app Flask (càd le fichier app.py).

@app.route('/lorem-ipsum/')
def lorem_ipsum () :
    page = """ 
        <!doctype html>
        <html lang="fr">
        <head>
                <meta charset="utf-8">
                <title>Lorem Ipsum</title>
        </head>
        <body>
                <h1>Lorem Ipsum</h1>
                <p>
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque condimentum at lectus at tristique. Ut dignissim congue enim. Ut tempus euismod lacus, eu blandit erat commodo ut. Pellentesque vestibulum, est nec pretium malesuada, mauris libero cursus purus, congue tempor tortor nisl eget augue. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Phasellus ac diam felis. Donec suscipit justo hendrerit ipsum eleifend rutrum. Aliquam dignissim at velit non tristique. Mauris ligula turpis, finibus ut condimentum ut, dapibus vel nulla. Cras imperdiet luctus ex ac placerat. Integer et rhoncus eros, id mattis metus. Vivamus gravida lacus et euismod tempus. 
                </p>
        </body>
        </html> 
    """
    return page

Lancer le serveur de dev

de Flask !

Pour accéder à la page web on a besoin un serveur web qui nous permette de liée une requête HTTP et Flask.

Plutôt que d'installer un serveur Apache, ou autre, Flask propose un serveur de développement intégré.

Ce serveur se lance sur le port 5000 et permet d'accéder directement à l'application Flask !

Attention : Un serveur de développement c'est bon pour le test rapide en dev. Pas pour la production !

Pour démarrer le serveur de production, rien de plus simple, cela est fait par la ligne "app.run..." du fichier app.py.

Par conséquent, il vous suffit de démarrer votre script app.py tout simplement en tapant la commande suivante (attention, le script doit être executable - chmod +x).

./app.py

Accéder à la page web.

Maintenant que le serveur est démarré, il ne vous reste plus qu'à accéder à la page web en vous rendant à l'adresse ci-dessous.

Utiliser le templating

Le templating c'est quoi

& ça sert à quoi ?

Le templating est un moyen d'écrire plus simplement des interfaces (par exemple des pages web) en le séparant du reste du code métier.

Séparer logique et apparence

Simplifier l'écriture des pages

Améliorer la lisibilité du code

Les templates dans Flask

Flask propose nativement un moteur de template : jinja2.

Pour pouvoir l'utiliser, importez render_template depuis Flask.

Pour utiliser un template, on créer un fichier dans le dossier "/templates/" dans lequel on écrit le code à afficher et on utilise la fonction render_template() de Flask avec en argument le nom du fichier.

Nous allons créer une copie de la page réalisée précédente pour utiliser un template. La page sera accessible à l'adresse "/lorem-ipsum/template/".

Pour cela, ajoutez le code ci-dessous au fichier app.py

@app.route('/lorem-ipsum-template/')
def lorem_ipsum_template () :
    return render_template('lorem-ipsum.html')

Et créez le fichier "lorem-ipsum.html" dans le dossier "templates" de l'application avec le code ci-dessous.

<!doctype html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <title>Lorem Ipsum</title>
</head>
<body>
    <h1>Lorem Ipsum</h1>
    <p> 
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque condimentum at lectus at tristique. Ut dignissim congue enim. Ut tempus euismod lacus, eu blandit erat commodo ut. Pellentesque vestibulum, est nec pretium malesuada, mauris libero cursus purus, congue tempor tortor nisl eget augue. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Phasellus ac diam felis. Donec suscipit justo hendrerit ipsum eleifend rutrum. Aliquam dignissim at velit non tristique. Mauris ligula turpis, finibus ut condimentum ut, dapibus vel nulla. Cras imperdiet luctus ex ac placerat. Integer et rhoncus eros, id mattis metus. Vivamus gravida lacus et euismod tempus.  
    </p>
</body>
</html> 

Passer des variables aux templates

Dans l'exemple précédent nous avons affiché un texte fixe, mais il est possible d'afficher un texte dynamique en transmettant des variables au templates.

Pour cela, il suffit de passer ces variables en tant qu'arguments à la fonction render_template().

Créons une copie de la fonction show-sentence nommée show-sentence-template qui affiche une phrase en fonction de l'URL tapée par l'utilisateur.

Pour cela, ajoutez la fonction ci-dessous à app.py

@app.route('/show-sentence-template/<sentence>/')
def show_sentence_template (sentence) :
    return render_template('show-sentence.html', sentence = sentence)

Et créez le fichier show-sentence.html dans le dossier templates et contenant le code ci-dessous.

<!doctype html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <title>Lorem Ipsum</title>
</head>
<body>
    You say : {{ sentence }}
</body>
</html>

Les fonctions de jinja2

Le moteur jinja2 propose de nombreuses fonctions pour introduire de la logique et de l'intelligence dans les templates.

L'objectif est de bien découper d'un coté la logique métier (code dans app.py) et la logique d'affichage qui ne doit être réalisée que dans les templates.

Presque toutes les fonctions de jinja2 utilisent les délimiteurs "{}".

Afficher une variable

Pour afficher une variable, on utilise la notation :

{{ variable }}

Utiliser des structures de contrôle

Les structures sont les mêmes qu'en python, if, else, elif, mais rajoute un tag de fin endif. On utilise la notation :

{% if True %}
  <h1>Vrai</h1>
{% elif None %}
  <h1>Null</h1>
{% else %}
  <h1>Faux</h1>
{% endif %}

Utiliser des boucles

{% for element in list %}
    {{ element }}
{% endfor %}

Il n'existe pas de boucle while en jinja2. La seule boucle disponible est la boucle for.

Utiliser des filtres

jinja2 permet d'appliquer des filtres à une variable.

Cela permet de modifier la variable, comme changer la casse, inverser un tableau, etc.

Pour ça on utilise simplement le pipe et le nom du filtre.

{{ variable|filtre }}
{{ titre_page|title }}
{{ password|hash('md5') }}

Par défaut, jinja échappe les variables pour vous protéger contre les attaques XSS.

Inclure et hériter des templates

Certaines parties de site web sont toujours identiques (en-tête, footer, etc.).

Pour ne pas avoir à ré-écrire le code, jinja permet d'inclure ou d'hériter des templates.

Hériter un template

En héritant un template, on définie une structure dans un template A que l'on va remplir depuis le template B.

Qu'il soit inclus ou étendu, un template à accès à toutes les variables définie dans le template depuis lequel il est appelé/étendu.

Pour créer un template à hériter, on renseigne les parties à remplir avec l'instruction block.

<!doctype html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <title>{% block titre %}Titre par défaut{% endblock %}</title>
</head>
<body>
    {% block body %}{% endblock %}
</body>
</html>

Inclure un template

Inclure un template permet de l'intégrer dans le template actuel. Cela est par exemple utile pour inclure des composants visuels, etc.

Pour inclure un template, il suffit d'utiliser la fonction include de jinja avec le nom du template à inclure.

<!doctype html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <title>Contact</title>
</head>
<body>
    {{ include('form_contact.html') }}
</body>
</html>

Pour hériter le template on utilise l'instruction extends avec le nom du template et on utilise à nouveau l'instruction block pour définir le contenu du block.

Fichier à hériter : layout.html

{% extends 'layout.html' %}

{% block titre %}
    Some page
{% endblock %}

{% block body %}
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed rutrum fermentum est nec dapibus. Cras vulputate lorem et quam gravida cursus.
{% endblock %}

Fichier depuis lequel on hérite : some_page.html

Utiliser un fichier CSS

ou javascript

Afin de modifier l'affichage ou le comportement d'une page, d’interagir avec l'utilisateur, etc., il est possible d'inclure un style CSS ou un script JavaScript.

Les fichiers CSS sont stockés dans le dossier "/static/css/" et les scripts dans "/static/javascript/".

Pour inclure le fichier on utilisera la fonction url_for().

Exemple : On va inclure dans le template layout un fichier css qui modifie l'apparence des formulaires pour ajouter une bordure.

<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">

Dans le fichier templates/layout.html, ajouter la ligne ci-dessous entre les balises head.

Puis on créer le fichier "/static/css/style.css" avec le code suivant :

form
{
    width: 50%;
    border: 7px solid #aaa;
    padding: 15px;
}

Utiliser des formulaires

Les types de formulaire

HTML propose de créer des formulaires pour permettre aux utilisateurs de transmettre des infos au site.

Il existe deux types de formulaires. Les formulaires GET et POST.

En GET les infos sont transmises dans l'URL.

En POST les infos sont transmises dans le corps de la requête HTTP.

Récupérer des données GET depuis Flask

Flask permet de récupérer des infos transmises en GET depuis l'objet request de Flask, que vous devrez donc importer au début de app.py.

Pour récupérer une donnée GET, on utilise request.args.get() en passant le nom de la donnée en argument.

Cette fonction retournera la valeur de la donnée GET si elle existe et None sinon.

Nous allons créer une page "/say/" qui affiche un formulaire GET et, s'il est soumis, le contenu soumis.

{% extends 'layout.html' %}

{% block titre %}
    Say page
{% endblock %}

{% block body %}
    <form action="" method="GET">
        <input name="say" type="text" placeholder="What do you want to say ?" /><button>Say it !</button>
    </form>
    {% if say %}
        You just said : {{ say }}
    {% endif %}
{% endblock %}

Puis ajoutez la fonction suivante dans app.py (n'oubliez pas d'importer request depuis Flask au début du fichier !)

@app.route('/say/')
def say () :
    say = request.args.get('say')
    return render_template('say.html', say = say)

Créez le fichier /templates/say.html qui contiendra le formulaire.

Récupérer des données POST depuis Flask

Flask permet de récupérer des infos transmises en POST depuis l'objet request de Flask, que vous devrez donc importer au début de app.py.

Pour récupérer une donnée POST, on utilise request.form.get() en passant le nom de la donnée en argument.

Cette fonction retournera la valeur de la donnée POST si elle existe et None sinon.

Nous allons créer une page "/contact/" qui affiche un formulaire POST et, s'il est soumis, le contenu soumis.

{% extends 'layout.html' %}

{% block titre %}
    Say page
{% endblock %}

{% block body %}
    <form action="" method="POST">
        <label for="email">Votre email :</label><br/>
        <input id="email" name="email" type="text"/><br/><br/>

        <label for="message">Votre message :</lable><br/>
        <textarea id="message" name="message"></textarea><br/><br/>

        <button>Envoyer</button>
    </form>

    {% if email and message %}
        Votre email : {{ email }}<br/>
        Votre message : {{ message }}
    {% endif %}
{% endblock %}

Puis ajoutez la fonction suivante dans app.py (n'oubliez pas d'importer request depuis Flask au début du fichier !)

 

Notez l'utilisation dans le décorateur de methods=['GET', 'POST']. Cela dit à Flask d'autoriser les requêtes POST.

@app.route('/contact/', methods=['GET', 'POST'])
def contact () :
    email = request.form.get('email')
    message = request.form.get('message')
    return render_template('contact.html', email = email, message = message)

Créez le fichier /templates/contact.html qui contiendra le formulaire.

Connecter une base de données

Créer notre base de données

1. Téléchargez le fichier de base de la base de données à l'adresse suivante :

https://bit.ly/2pFUxSP

2. Exécuter le fichier dans MySQL.

mysql -u root -p < create_database.sql

Connection à MySQL

Par défaut Flask n'intègre pas d'ORM. Afin de simplifier l'apprentissage, nous n'en utiliserons pas non plus.

Si vous avez besoin d'un ORM, il est courrent d'utilise SQLAlchemy avec flask-sqlalchemy.

Nous allons créer une fonction connect_db() qui se connectera à la base de donnée, ouvrira un cursor et le renverra.

Au début de app.py, ajoutez donc la fonction ci-dessous.

def connect_db () :
    g.mysql_connection = mysql.connector.connect(
        host = app.config['DATABASE_HOST'],
        user = app.config['DATABASE_USER'],
        password = app.config['DATABASE_PASSWORD'],
        database = app.config['DATABASE_NAME']
    )   

    g.mysql_cursor = g.mysql_connection.cursor()
    return g.mysql_cursor

Comme dans le cours précédent, nous utiliserons MySQL seul. Pensez donc à importer mysql.connector !

Ajoutez aussi les variables nécessaires dans /secret_config.py

#Database config
DATABASE_HOST = 'localhost'
DATABASE_USER = 'root'
DATABASE_PASSWORD = 'your_password'
DATABASE_NAME = 'cours_iot_python_pour_le_web'

Nous allons aussi créer une fonction get_db() qui sera chargée de retourner un accès à la base de donnée, et d'en retourner toujours un et un seul.

Après connect_db(), ajoutez donc la fonction ci-dessous :

def get_db () :
    if not hasattr(g, 'db') :
        g.db = connect_db()
    return g.db

Note : on ne stock pas la variable directement, mais on la stock en passant par la variable "g" (ex : g.mysql_connection).

Ce cas est spécifique à Flask. g est une variable qui doit être importée depuis Flask est qui est accessible depuis partout dans Flask. Elle a comme particularité de ne pas disparaître à la fin d'une fonction.

Connection à MySQL

Faire une requête MySQL

Nous allons maintenant créer une page "/show-entries/" qui va afficher toutes les lignes de la table "entries".

Pour faire une requête sur la base de données, il suffit d'utiliser la fonction get_db() pour récupérer la base et faire comme en Python pure.

Créez la fonction show_entries() ci-dessous :

@app.route('/show-entries/')
def show_entries () :
    db = get_db()
    db.execute('SELECT name, value FROM entries')
    entries = db.fetchall()
    return render_template('show-entries.html', entries = entries)

Créez le fichier "/templates/show-entries.html"

{% extends 'layout.html' %}

{% block titre %}
    Show entries
{% endblock %}

{% block body %}
    <h1>That's all the entries</h1>
    <ul>
        {% for entrie in entries %}
            <li>{{ entrie.0 }} = {{ entrie.1 }}</li>
        {% endfor %}
    </ul>
{% endblock %}

Fermer connexion MySQL

Pour cela, Flask met à disposition un décorateur permettant d'appeler une fonction à la fin de l’exécution de l'application.

À la fin d'un script, vous devez fermer la connexion à MySQL pour ne pas l'encombrer.

Nous allons donc utiliser ce décorateur sur la fonction close_db() qui va fermer la connexion si elle existe.

@app.teardown_appcontext
def close_db (error) :
    if hasattr(g, 'db') :
        g.db.close()

Utiliser les sessions et créer une page de connexion

Les sessions c'est quoi ?

Sur un site web, on a besoin de stocker & transmettre des infos spécifique à l'utilisateur tout le temps (ex : savoir qu'il est connecté).

Pour cela, plusieurs solutions sont possibles, mais le mieux est d'utiliser des sessions. L'utilisateur a un cookie unique & les données liées sont stockées sur le serveur.

Les sessions dans Flask

Flask intègre un système de session qui est très simple à utiliser.

Toutes les données de session sont stockées dans la variable session (que vous devrez importer depuis Flask au début du fichier).

Pour pouvoir utiliser une session avec Flask, vous devez avoir définit la secret_key de votre app. Pour cela, ajoutez la ligne ci-dessous dans /secret_config.py.

SECRET_KEY = 'some random string w17h n|_|m83r5'

Définir une variable de session

Pour définir une variable de session, il suffit de faire session['nom_variable'] = 'valeur'.

Pour voir son utilisation, nous allons créer une page '/login/' qui proposera à un utilisateur enregistré dans la table "user" de la base de données de se connecter.

Créons d'abord le template '/templates/login.html' ci-dessous.

{% extends 'layout.html' %}

{% block titre %}
    Connexion
{% endblock %}

{% block body %}
    <h1>Connexion</h1>
    <form action="" method="POST">
        <fieldset>
            <label for="email">Email :</label><br/>
            <input type="text" name="email" id="email" /><br/><br/>

            <label for="password">Password :</label><br/>
            <input type="password" name="password" id="password" /><br/><br/>

            <input type="submit" value="Connecter"/>
        </fieldset>
    </form>

    {% if user %}
        Bonjour {{ user.0 }}, vous êtes bien connecté !
    {% endif %}
{% endblock %}

Nous allons créer la fonction login() qui devra afficher cette page et vérifier s'il existe un utilisateur avec ce couple email/password.

Pour cela, nous allons devoir utiliser un algorithme permettant de chiffrer les mots de passe.

Installez donc la bibliothèque passlib & argon2 :

sudo pip3 install passlib
sudo pip3 install argon2_cffi

Et importez argon2 depuis passlib au début de app.py comme ceci :

from passlib.hash import argon2

Créons maintenant la fonction login() :

@app.route('/login/', methods = ['GET', 'POST'])
def login () :
    email = str(request.form.get('email'))
    password = str(request.form.get('password'))

    db = get_db()
    db.execute('SELECT email, password, is_admin FROM user WHERE email = %(email)s', {'email' : email})
    users = db.fetchall()

    valid_user = False
    for user in users :
        if argon2.verify(password, user[1]) :
            valid_user = user
     
    if valid_user :
        session['user'] = valid_user
        return redirect(url_for('admin'))

    return render_template('login.html')

Récupère les données POST email & password et les forces en texte.

Fait une requête pour charger tous les utilisateurs avec l'email transmis.

Pour chaque résultat, on vérifie si le password fourni correspond bien à celui stocké en base pour l'utilisateur.

Si on a bien un utilisateur valide, on le stock en session et on redirige vers la page d'administration.

Accéder à une variable de session

Pour accéder à une variable de session, il suffit d'utiliser la fonction session.get() en lui passant en argument le nom de la variable.

Pour illustrer cela, nous allons créer une page "/admin/" qui ne sera affichée que si l'utilisateur est un administrateur connecté.

Pour cela, créez le template "/templates/admin.html"

{% extends 'layout.html' %}

{% block titre %}
    Administration
{% endblock %}

{% block body %}
    <h1>Administration</h1>
    <p>Bienvenue sur l'administration !</p>

    <a href="{{ url_for('admin_logout') }}">Déconnexion</a>
{% endblock %}

Et la fonction admin() :

@app.route('/admin/')
def admin () :
    if not session.get('user') or not session.get('user')[2] :
        return redirect(url_for('login'))

    return render_template('admin.html', user = session['user'])

Détruire une session

Une fois qu'une personne ne veux plus être identifiée (par exemple si elle se déconnecte), il peut être nécessaire de détruire la session.

Pour cela, il suffit d'utiliser la fonction session.clear().

@app.route('/admin/logout/')
def admin_logout () :
    session.clear()
    return redirect(url_for('login'))

Mettre en ligne son site Flask avec Apache

Pourquoi utiliser Apache ?

Pour mettre votre projet en ligne, vous ne pouvez pas utiliser directement le serveur de dev fourni par Flask.

En effet, celui-ci présente de mauvaises performances, une maintenance peu pratique, une configuration limitée, etc.

Par conséquent, nous devons utiliser un serveur plus adapté, et Apache étant assez simple à configurer c'est celui que nous utiliserons.

Créer un fichier WSGI

Quand un serveur comme Apache doit transmettre des requêtes à une application comme Flask, il se repose en fait sur la norme WSGI qui définie la façon dont ils devront discuter.

De notre coté, nous allons devoir créer un fichier /app.wsgi qui sera appelé par Apache lors d'une requête et qui exécutera notre application Flask.

Cette norme est spécifique à Python.

Illustration piquée à sametmax.com

import sys, os
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))

from app import app as application

Ajoute le chemin de l'application au Python Path.

Importe la variable app depuis le fichier app.py sous le nom application (c'est le nom application qui permet à WSGI de trouver l'app).

Installer Apache2 et le module WSGI

Pour pouvoir utiliser Apache2 avec Flask il faut installer à la fois Apache et le module WSGI.

Pour cela, il suffit d'utiliser le gestionnaire de paquet Linux.

sudo apt-get install apache2 libapache2-mod-wsgi-py3

Écrire le fichier de configuration Apache

Maintenant, il faut dire à Apache où trouver l'application, comment la lancer, etc.

Pour cela, on va écrire un fichier de configuration Apache qui va créer un VirtualHost.

Une fois que ce fichier sera écrit, on le copiera dans la configuration d'Apache et on rechargera ce dernier.

Pour commencer nous allons écrire le fichier /apache.conf dans le dossier du projet Flask.

<VirtualHost *:80>
    ServerName localhost

    WSGIDaemonProcess app user=www-data group=www-data threads=5
    WSGIScriptAlias / /path/for/projet/flask/app.wsgi
    

    <Directory /path/for/projet/flask/>
        WSGIProcessGroup app 
        WSGIApplicationGroup %{GLOBAL}
        Require all granted
    </Directory>
</VirtualHost>

Pour le serveur locale on laisse localhost, mais en prod il faudra mettre le nom de domaine (ex : example.com)

Remplacez ce chemin par le vrai chemin de votre projet. Par exemple /var/www/html/projet-flask/.

Activer le fichier de configuration Apache

Il ne reste plus qu'à activer le fichier de configuration Apache.

Pour cela, il suffit de copier le fichier apache.conf dans le dossier /etc/apache2/sites-available/ puis de faire un liens symbolique vers sites-enabled/, et enfin de re-démarrer Apache.

sudo cp apache.conf /etc/apache2/sites-available/flask.conf
sudo ln -s /etc/apache2/sites-available/flask.conf /etc/apache2/sites-enabled/flask.conf
sudo rm /etc/apache2/sites-enabled/000-default.conf
sudo /etc/init.d/apache2 restart

Accéder au site

Pour voir le site, il ne vous reste plus qu'à aller à l'adresse http://localhost

Résoudre les erreurs

Si vous avez une erreur HTTP (500 notamment), lancez la commande ci-dessous et rafraîchissez la page pour voir les logs d'erreur Apache.

Si Apache ne redémarre pas, vous pouvez voir l'erreur avec la commande ci-dessous.

systemctl status apache2.service
tail -f /var/log/apache2/error.log

Si vous galérez, appelez nous, on est là pour ça !

TP : Créer un site de monitoring de sites web

Fonctionnalités

Lister et gérer

les sites

Mise à jour auto du

statut des sites

Envoyer un message

si K.O

Lister et gérer

Liste accessible publiquement + consultation d'un site particulier et de son historique.

Gestion sécurisée via une connexion admin.

Présence des fonctions suivantes :

- Ajouter

- Modifier

- Supprimer

Mise à jour du statut

Mise à jour entièrement automatique et faîtes toutes les 2 minutes avec historique.

Basée sur les codes HTTP de retour. Avec 4 status :

- Inaccessible -> 4xx

- Erreur serveur -> 5xx

- Accessible -> 2xx

- Impossible de joindre le serveur -> 999

Envoie des messages

Envoie des messages sur serveur inaccessible ou erreur serveur 3 fois de suite.

Envoie par slack & telegram.

- Stockage des infos de connexion dans secret_config.py

- Pas d'envoi de message si un message a déjà été envoyé pour le site il y a moins de 2h.

Questions ?

Python pour le web

By plebweb

Python pour le web

  • 981