05 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:
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("boardgames.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 Boardgame
:
class Boardgame(BaseModel):
name = CharField()
slug = CharField()
description = TextField()
release_date = 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([Boardgame, ])
def drop_tables():
with database:
database.drop_tables([Boardgame, ])
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 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
. Et le paquet awesome-slugify
également.
Puis au sein de notre fichier app.py
nous allons créer la fonction suivante:
@app.cli.command()
def fakedata():
from faker import Faker
from slugify import slugify
fake = Faker()
for pk in range(0, 42):
name = fake.company()
Boardgame.create(name=name,
slug=slugify(name, to_lower=True)
description=fake.catch_phrase(),
release_date=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 jeux de plateaux au sein d'un fichier de templating boardgames.html.
Voici le code permettant de récupérer l'ensemble des jeux présent en base:
Boardgame.select()
Cette ligne permet de récupérer tout les jeux présent en bases. A vous maintenant d'itérer sur la liste retourner afin d'obtenir une liste des jeux au sein de votre template.
Associations
Nous allons créer la classe python qui correspond à notre table Category
puis définir la clé étrangère entre Boardgame
et Category
:
class Category(BaseModel):
name = CharField()
slug = CharField()
created_at = DateTimeField(default=datetime.datetime.now)
class Boardgame(BaseModel):
name = CharField()
slug = CharField()
description = TextField()
release_date = DateField()
created_at = DateTimeField(default=datetime.datetime.now)
category = ForeignKeyField(Category, backref="boardgames")
N'oubliez pas d'ajouter dans les fonctions de création et de suppression des tables la classe Category.
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):
name = fake.company()
Category.create(name=name,
slug=slugify(name, to_lower=True))
for category in Category.select():
for pk in range(0, 4):
name = fake.company()
Boardgame.create(name=name,
slug=slugify(name, to_lower=True)
description=fake.catch_phrase(),
release_date=fake.date(),
category=category)
Ainsi nous pouvons obtenir le nom de la catégorie d'un jeux via:
boardgame.category.name
Ou encore la liste des jeux d'une catégorie via:
category.boardgames
À vous de jouer
- Créer une route pour afficher l'ensemble des jeux avec leur catégorie;
- Créer une route pour afficher l'ensemble des catégorie et les jeux associés;
- Créer une vue pour afficher uniquement les informations d'un seul jeux en prenant en paramètre (
slug
) le slug du jeux 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.