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

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

L’application va utiliser une base de données SQLite pour stocker les informations liées au projet. Python intègre un support pour SQLite dans le module sqlite3.

SQLite est intéressant, car il peut s’utiliser sans l’installation d’un serveur de base de données séparé 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, parce que 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. N'hésitez pas à retourner lire le "Les bases de données SQL", les slides des deux cours sur le SQL sur Moodle, ou la documentation de SQL.

Connexion à 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 connexion 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.

mobility/db.py

import sqlite3
from flask import current_app, g


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ée. 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. Pour votre projet, la base de données, vous sera fournie sur Moodle, vous ne devrez donc pas créer de table vous-même. Nous pouvons créer une base de données contenant une table country en utilisant les commandes SQL ci-dessous:

mobility/schema.sql

DROP TABLE IF EXISTS country;
CREATE TABLE country(iso_country TEXT PRIMARY KEY, name TEXT);
INSERT INTO country(iso_country,name) VALUES("FI", "FINLAND");

Vous pouvez voir qu'en plus de créer la table country, le code ci-dessus insère également un pays (la Finlande). Vous pouvez exécuter ces commandes SQL en les intégrant comme ci-dessous dans le fichier db.py:

mobility/db.py

def get_db():
    """Returns the database connection. Create the connection if needed.

    Returns:
        db: The db connection to be used for SQL functions
    """

    # g is the shorthand for "globals" and allows registering available in the whole Flask app
    if 'db' not in g:
        # If it's not there, let's create the db connection
        g.db = sqlite3.connect(
            current_app.config['DATABASE'],
            detect_types=sqlite3.PARSE_DECLTYPES
        )
        print(current_app.config['DATABASE'])

        # Instead of getting "tuple" out of queries, we'll get dictionaries of column->value
        g.db.row_factory = sqlite3.Row

    return g.db

def close_db(e=None):
    """Close the database

    Args:
        e: unused
    """
    db = g.pop('db', None)

    if db is not None:
        db.close()


def init_db():
    db = get_db()
    # Not necessary for the project
    with current_app.open_resource('schema.sql') as f:
        db.executescript(f.read().decode('utf8'))


def init_app(app):
    app.teardown_appcontext(close_db)

open_resource() ouvre un fichier qui est relatif au package mobility. 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 connexion à la base de données, qui est utilisée pour exécuter les commandes lues dans le fichier passé en argument.

Enregistrement à l’application

La fonction close_db doit être enregistrée dans l’instance de l’application. Sinon, elle ne peut pas être utilisée par l’application.

mobility/db.py

def init_app(app):
      app.teardown_appcontext(close_db)

app.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.

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.

Si ce n'est pas fait, définissez également la variable DATABASE ainsi que le code permettant de créer le répertoire instance.

mobility/__init__.py

def create_app():
      app = ...

      # existing code omitted

      app.config.from_mapping(
               SECRET_KEY='dev',
               DATABASE=os.path.join(app.instance_path, 'db.sqlite')
            )


      # existing code omitted
      # ensure the instance folder exists
      try:
          os.makedirs(app.instance_path)
      except OSError:
          pass


      from . import db
      db.init_app(app)

      with app.app_context():
         db.init_db()

      return app

Continuez en lisant le document Plans et vues pour les pays.