Construire et accéder à la base de données

L’application va utiliser une base de données SQLite pour stocker les utilisateurs et les messages du blog. Python intègre un support pour SQLite dans le module sqlite3.

SQLite est intéressant car il peut s’utiliser sans nécessiter l’installation d’un serveur de base de données séparés et il est directement intégré à Python. Cependant, si plusieurs accès en écriture à la base de données se font en même temps, l’application va ralentir car ceux-ci doivent être séquentiels. Pour de petites applications, ce n’est pas important. Si votre application prend de l’ampleur et nécessite beaucoup d’écritures, vous devrez peut-être passer à une autre base de données.

Ce tutoriel ne rentre pas dans les détails du langage SQL. Si vous n’êtes pas familiers avec celui-ci, il est décrit dans la documentation de SQL, voir le document language.

Connection à la base de données

La première chose à faire pour travailler avec une base de données SQLite (et la plupart des autres librairies Python de gestion de bases de données) est de créer une connexion avec la base de données. Toutes les requêtes et les opérations se font en utilisant la connection qui est fermée lorsque le travail est terminé.

Dans une application web, cette connexion est généralement associée à la requête. Elle est créée pendant le traitement de la requête et fermée avant l’envoi de la réponse.

flaskr/db.py
import sqlite3

import click
from flask import current_app, g
from flask.cli import with_appcontext


def get_db():
    if 'db' not in g:
        g.db = sqlite3.connect(
            current_app.config['DATABASE'],
            detect_types=sqlite3.PARSE_DECLTYPES
        )
        g.db.row_factory = sqlite3.Row

    return g.db


def close_db(e=None):
    db = g.pop('db', None)

    if db is not None:
        db.close()

g est un objet spécial qui est unique pour chaque requête. Il est utilisé pour stocker des données qui pourraient être accédées par plusieurs fonctions durant le traitement de la requête. La connexion est conservée et réutilisée si get_db est appelé une deuxième fois dans le traitement de la même requête.

current_app est un autre objet spécial qui pointe vers l’application Flask qui traite la requête. Comme vous avez utilisé une usine à application, il n’y a pas d’objet application en écrivant la suite de votre code. get_db sera appelée lorsque l’application a été créée et qu’elle traite une requête. De cette façon, current_app peut être utilisé.

sqlite3.connect() crée une connexion avec le fichier correspondant au paramètre de configuration DATABASE. Il n’est pas nécessaire que ce fichier existe. Il sera créé plus tard lors de l’initialisation de la base de données.

Le paramètre sqlite3.Row indique que la connexion doit retourner des lignes qui sont équivalentes à des dictionnaires Python. Cela permet d’accéder aux colonnes en indiquant leur nom.

close_db vérifie si une connexion a été créée en regardant si g.db a été initialisé. Si la connexion existe, elle est fermé. Vous apprendrez plus tard à votre application d’utiliser la fonction close_db depuis l’usine à applications de façon à l’appeler automatiquement après le traitement de chaque requête.

Création des tables

Dans SQLite, les données sont stockées dans des tables et des colonnes. Elles doivent être créées avant que vous ne puissiez stocker et récupérer des données dans la base de données. Flask va stocker les utilisateurs dans la table user et les messages dans la table posts. Une telle base de données peut être créée en utilisant les commandes SQL ci-dessous:

flaskr/schema.sql
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS post;

CREATE TABLE user (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  username TEXT UNIQUE NOT NULL,
  password TEXT NOT NULL
);

CREATE TABLE post (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  author_id INTEGER NOT NULL,
  created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  title TEXT NOT NULL,
  body TEXT NOT NULL,
  FOREIGN KEY (author_id) REFERENCES user (id)
);

Vous pouvez exécuter ces commandes SQL en les intégrant comme ci-dessous dans le fichier db.py:

flaskr/db.py
def init_db():
    db = get_db()

    with current_app.open_resource('schema.sql') as f:
        db.executescript(f.read().decode('utf8'))


@click.command('init-db')
@with_appcontext
def init_db_command():
    """Clear the existing data and create new tables."""
    init_db()
    click.echo('Initialized the database.')

open_resource() ouvre un fichier qui est relatif au package flaskr. L’utilisation d’un chemin relatif est intéressant car vous ne saurez pas nécessairement quel sera ce répertoire lorsque vous mettrez votre application en production. get_db retourne une connection à la base de données, qui est utilisée pour exécuter les commandes lues dans le fichier passé en argument.

click.command() définit uns interface en ligne de commande baptisée init-db qui appelle la fonction init_db and affiche un message de réussite à l’utilisateur. Vous pouvez lire le document :ref:`cli`pour en savoir plus sur comment écrire de telles commandes.

Enregistrement à l’application

Les fonctions close_db et init_db_command doivent être enregistrée dans l’instance de l’application. Sinon, elles ne peuvent pas être utilisées par l’application. Cependant, puisque vous utiliser une fonction de type usine, cette instance n’est pas disponible lors de l’écriture des fonctions. Vous devez plutôt écrire une fonction qui prend une application comme argument et l’enregistre.

flaskr/db.py
def init_app(app):
    app.teardown_appcontext(close_db)
    app.cli.add_command(init_db_command)

:meth:`app.teardown_appcontext() <Flask.teardown_appcontext>`demande à Flask d’appeler cette fonction lorsqu’elle libère les ressources associées à une requête après avoir retourné la réponse.

app.cli.add_command() ajoute une nouvelle commande qui peut être appelée par la commande flask.

Vous devez importer et appeler cette fonction depuis l’usine à applications. Placez le nouveau code à la fin de la fonction contenant l’usine à applications et juste avant de retourner l’application.

flaskr/__init__.py
def create_app():
    app = ...
    # existing code omitted

    from . import db
    db.init_app(app)

    return app

Initialisation de la base de données

Maintenant que la fonction init-db a été enregistrée dans l’application, elle peut être lancée en utilisant la commande flask, similaire à la commande run de la page précédente.

Note

Si le serveur que vous aviez lancé pour la page précédente tourne toujours, vous pouvez soit l’arrêter, soit lancer cette commande dans un nouveau terminal. Si vous utilisez un nouveau terminal, n’oubliez pas d’aller dans le répertoire de votre projet et d’activer l’environnement virtuel comme expliqué dans Activate the environment. Vous devrez aussi initialiser les variables d’environnement FLASK_APP et FLASK_ENV comme expliqué dans la page précédente.

Lancer la commande init-db:

$ flask init-db
Initialized the database.

Il y a maintenant un fichier dénommé flaskr.sqlite dans le répertoire instance de votre projet.

Continuez en lisant le document Plans et vues.