DY

DelahayeYourself

modules / LP Web Dynamique /

TP08: ORM

Un ORM est une technique de programmation informatique qui crée l'illusion d'une base de données orientée objet à partir d'une base de données relationnelle en définissant des correspondances entre cette base de données et les objets du langage utilisé. On pourrait le désigner par « correspondance entre monde objet et monde relationnel ».

Flask et les ORMs

Flask ne fournit pas d'ORM par défault, il laisse la faculté à l'utilisateur de choisir la librairie qui lui convient.

Découverte de Peewee

Peewee est un (petit) ORM simple. Il a peu de concepts (mais expressifs), ce qui le rend facile à apprendre et intuitif à utiliser. Celui-ci est open source et disponible à cette adresse.

Installation

Comme toujours un simple pipenv install peewee

La base de données

Pour la suite de ce TP nous allons créer la base de données SQLite suivante:

Ingen UML Database Scheme
Diagramme UML de la base de données d'InGen
Configuration

Après avoir installé le paquet peewee il suffit de configurer une connexion à notre base de données.

A la racine de votre projet créer un fichier models.py avec le contenu suivant:

from peewee import *

database = SqliteDatabase("data.sqlite3")
Notre premier Model

Nous définissons d'abord une classe BaseModel qui permet de spécifier la connexion à la BDD à tous nos modéles.

class BaseModel(Model):

    class Meta:
        database = database

Puis de créer directement notre classe Dinosaur:

class Dinosaur(BaseModel):

    name = CharField()
    birthday = DateField()
    created_at = DateTimeField(default=datetime.datetime.now)

Voilà ! Notre model est créé, à nous de l'utiliser !

Création de la base

Avant de commencer à requêter sur notre base, il convient de la .. créer !

Pour ce faire nous allons créer deux fonctions dans notre fichier models.py, l'une pour créer les tables et l'autre pour les détruire:

def create_tables():
    with database:
        database.create_tables([Dinosaur, ])


def drop_tables():
    with database:
        database.drop_tables([Dinosaur, ])

Enfin nous allons créer les commandes python associer pour créer/détruire les tables au sein du fichier app.py à la fin du fichier ajouter les lignes:

@app.cli.command()
def initdb():
    """Create database"""
    create_tables()
    click.echo('Initialized the database')

@app.cli.command()
def dropdb():
    """Drop database tables"""
    drop_tables()
    click.echo('Dropped tables from database')

Il suffit d'appeller ces commandes dans notre shell:

$ flask initdb
$ flask dropdb
Création de données

Nous pourrions écrire des données à la main pour remplir notre base de données mais cela serait long et ennuyeux, nous allons donc utiliser la puissance de l'ORM et une librairie de création de fausses données pour créer différents enregistrements.

Faker

Faker est un paquet Python qui génère de fausses données. Que vous deviez amorcer votre base de données, créer de beaux documents XML, compléter votre persistance pour la soumettre à un test de résistance, ou anonymiser des données provenant d'un service en production, Faker est fait pour vous.

Comme d'habitude on installe le paquet faker via pipenv install faker.

Puis au sein de notre fichier app.py nous allons créer la fonction suivante:

@app.cli.command()
def fakedata():
    from faker import Faker
    fake = Faker()
    for pk in range(0, 42):
        Dinosaur.create(name=fake.first_name(), 
                        birthday=fake.date())

Puis pour lancer la commande:

$ flask fakedata
Requête via un Model

Pour plus d'informations sur la manière d'écrire des requêtes, je vous invite à vous rendre à cette page de la documentation.

Dans le fichier app.py nous allons créer une route pour afficher l'ensemble des dinosaures au sein d'un fichier de templating dinos.html.

Voici le code permettant de récupérer l'ensemble des dinosaures présent en base:

Dinosaur.select()

Cette ligne permet de récupérer tout les dinosaures présent en bases. A vous maintenant d'itérer sur la liste retourner afin d'obtenir une liste des dinosaures au sein de votre template.

Associations

Nous allons créer la classe python qui correspond à notre table Model puis définir la clé étrangère entre Dinosaur et Model:

class IngenModel(BaseModel):
    version = FloatField()
    created_at = DateTimeField(default=datetime.datetime.now)


class Dinosaur(BaseModel):

    name = CharField()
    birthday = DateField()
    created_at = DateTimeField(default=datetime.datetime.now)
    model = ForeignKeyField(IngenModel, backref="dinosaurs")

N'oubliez pas d'ajouter dans les fonctions de création et de suppression des tables la classe IngenModel.

Puis nous allons mettre à jour notre générateur de données:

@app.cli.command()
def fakedata():
    from faker import Faker
    fake = Faker()

    for pk in range(0, 5):
        IngenModel.create(version=abs(fake.pyfloat(left_digits=1, 
                                                   right_digits=1)))
    for model in IngenModel.select():
        for pk in range(0, 4):
            Dinosaur.create(name=fake.first_name(), 
                            birthday=fake.date(),
                            model=model)

Ainsi nous pouvons obtenir la version du modéle d'un dinosaure via:

dinosaur.model.version

Ou encore la liste des dinosaures d'un modéle via:

model.dinosaurs

À vous de jouer

  1. Créer une route pour afficher l'ensemble des dinosaures avec leur modéle;
  2. Créer une route pour afficher l'ensemble des modéles et les dinosaures associés;
  3. Créer la classe pour l'espèce et modifier la classe IngenModel (création/suppresion de tables ?);
  4. Créer une vue pour afficher uniquement les informations d'un seul dinosaure en prenant en paramètre (id) l'identifiant du dinosaure cf la partie sur le requêtage de la documentation.

Conclusion

Nous venons de voir que manipuler une base de données est extrèmement simple avec les bons outils, je vous invite donc à lire la documentation de Peewee notamment sur la partie Querying.