Flask-SQLAlchemy et Flask-Migrate : Le Guide de l’ORM
Lorsque l’on développe une application Flask, le choix de la gestion des données est crucial.
Pour transformer vos objets Python en tables de base de données sans écrire une seule ligne de SQL, le duo Flask-SQLAlchemy et Flask-Migrate est l’outil standard de l’écosystème.
1. Flask-SQLAlchemy : La puissance de l’ORM
Flask-SQLAlchemy simplifie l’utilisation d’SQLAlchemy. Un ORM (Object Relational Mapper) permet d’interagir avec votre base de données en manipulant des classes Python.
Pourquoi l’adopter ?
- Abstraction : Plus besoin de jongler avec des requêtes SQL complexes.
- Flexibilité : Changez de moteur de base de données (SQLite vers PostgreSQL) avec une simple ligne de configuration.
- Intégration : Conçu spécifiquement pour s’intégrer dans le cycle de vie d’une application Flask.
2. Flask-SQLAlchemy et Flask-Migrate : Explication et Utilisation
Lorsque l’on développe une application Flask qui nécessite une base de données, il est important de choisir une bibliothèque pour interagir avec cette base et pour gérer les évolutions du schéma.
Flask-SQLAlchemy et Flask-Migrate sont deux extensions essentielles pour cela.
Avec Flask, la gestion des migrations de base de données se fait généralement avec Flask-Migrate, une extension basée sur Alembic.
Elle permet de suivre et d’appliquer les modifications du schéma de la base de données sans perdre les données existantes.
3. Flask-SQLAlchemy : Gestion de la Base de Données
📌 Qu’est-ce que Flask-SQLAlchemy ?
Flask-SQLAlchemy est une extension qui simplifie l’utilisation d’SQLAlchemy dans une application Flask.
SQLAlchemy est un ORM (Object Relational Mapper) puissant qui permet d’interagir avec une base de données en utilisant des objets Python au lieu d’écrire des requêtes SQL brutes.
🚀 Pourquoi utiliser Flask-SQLAlchemy ?
- Facilite la gestion des bases de données en Flask.
- Permet d’écrire des requêtes SQL de manière plus intuitive en Python.
- Prend en charge plusieurs types de bases de données (SQLite, PostgreSQL, MySQL, etc.).
- Fournit un système de migration facile à utiliser avec Flask-Migrate.
🔧 Exemple de configuration de Flask-SQLAlchemy
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db' # Connexion à SQLite
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app) # Création de l'instance SQLAlchemy
# Définition d'un modèle pour une table "users"
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
# Création de la base de données (uniquement lors de la première exécution)
with app.app_context():
db.create_all()Python4. Flask-Migrate : Gestion des Migrations
📌 Qu’est-ce que Flask-Migrate ?
Flask-Migrate est une extension qui permet de gérer les migrations de base de données en SQLAlchemy avec Alembic.
Une migration permet d’apporter des modifications à la structure de la base de données (ajout de colonnes, suppression de tables, modification de types de données, etc.) sans perdre les données existantes.
🚀 Pourquoi utiliser Flask-Migrate ?
– Permet d’appliquer des changements sur la base de données de manière progressive. – Facilite la gestion des versions du schéma de base de données. – Évite d’avoir à supprimer et recréer la base de données à chaque modification. —
Pour faire simple, Flask-Migrate (qui est une surcouche de l’outil Alembic) est l’équivalent de Git, mais pour le schéma de votre base de données.
Au lieu de modifier votre base de données à la main avec des requêtes SQL (ALTER TABLE, CREATE TABLE) — ce qui est dangereux et intraçable en production —, vous modifiez vos classes Python. Flask-Migrate se charge ensuite de calculer la différence (« diff ») entre votre code et la base de données, et de générer un script pour appliquer ces changements proprement.
📌 Étapes pour utiliser `flask db migrate`
- Voici comment fonctionne la migration avec Flask :
- Installation de Flask-Migrate
- Si ce n’est pas déjà fait, installez Flask-Migrate et SQLAlchemy : « bash pip install flask-migrate flask-sqlalchemy « `
- Initialisation du projet
- Il faut d’abord lier Flask, votre ORM (SQLAlchemy) et l’outil de migration :
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db' # Ou autre SGBD (PostgreSQL, MySQL, etc.)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
migrate = Migrate(app, db) # Intégration avec Flask-Migrate
# Définition d’un modèle
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
completed = db.Column(db.Boolean, default=False)
if __name__ == '__main__':
app.run(debug=True)
PythonInitialisation de Flask-Migrate
(À ne faire qu’une seule fois par projet)
C’est l’équivalent de git init.
flask db initBash👉 Cela crée un dossier migrations/ à la racine de votre projet qui contiendra tout l’historique de votre base de données.
Générer une migration
L’équivalent d’un git commit.
Imaginons que vous ajoutiez une colonne due_date à votre classe Task. Pour que la base de données soit au courant de ce changement de schéma, vous lancez :
flask db migrate -m "Ajout du modèle Task"👉 Flask-Migrate analyse vos modèles Python, voit qu’il y a une différence avec la base actuelle, et génère un script Python dans le dossier migrations/versions/. À ce stade, la base de données n’est pas encore modifiée.
Appliquer la migration
L’équivalent d’un déploiement en production.
Pour exécuter le script généré à l’étape précédente et altérer physiquement le schéma de la base de données, on utilise :
flask db upgrade👉 Cela exécute le script de migration et met à jour la base.
Gérer les modifications du modèle
Si vous modifiez un modèle (ex: ajouter une colonne `due_date` à `Task`) :
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
completed = db.Column(db.Boolean, default=False)
due_date = db.Column(db.DateTime) # Nouvelle colonnePythonExécutez les migrations :
flask db migrate -m "Ajout de due_date à Task"
flask db upgradeRollback en cas d’erreur :Optionnel.
Si la modification casse quelque chose ou que vous souhaitez revenir à l’état précédent du schéma, vous pouvez annuler la dernière migration :
flask db downgrade🛠 Commandes utiles
5. Résumé des commandes
| Commande | Description |
|---|---|
| flask db init | Initialise le dossier `migrations` |
| flask db migrate -m « Message » | Génère un fichier de migration basé sur les modifications du modèle |
| flask db upgrade | Applique la migration à la base de données |
| flask db downgrade | Annule la dernière migration |
| flask db history | Affiche l’historique des migrations |
| flask db current | Montre la version actuelle appliquée |
6. Exemples d’utilisation
Ajout d’une colonne dans un modèle existant
Si l’on souhaite ajouter une nouvelle colonne age au modèle User, il suffit de :
Modifier le modèle Python :
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
age = db.Column(db.Integer, nullable=True) # Nouvelle colonnePythonGénérer une nouvelle migration :
flask db migrate -m "Ajout de la colonne age à User"Appliquer la migration :
flask db upgrade6. Résumé
| Outil | Rôle |
|---|---|
| Flask-SQLAlchemy | Gère la connexion et l’interaction avec la base de données via un ORM |
| Flask-Migrate | Permet d’appliquer des modifications au schéma de la base sans perdre de données |
7. Modéliser les relations entre données
La puissance d’une base relationnelle réside dans les liens entre les tables. Voici comment les implémenter proprement :
Relation One-to-Many (1 → N)
C’est la relation la plus commune (ex: Un utlisateur possède plusieurs articles).
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
articles = db.relationship('Article', backref='author', lazy=True)
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))Pythondb.relationship() est utilisé côté « parent » (ici `User`).
db.ForeignKey()` est utilisé côté « enfant » (ici `Article`).
Relation One-to-One (1 → 1)
Exemple : Un utilisateur a un seul profil.
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
profile = db.relationship('Profile', backref='user', uselist=False)
class Profile(db.Model):
id = db.Column(db.Integer, primary_key=True)
bio = db.Column(db.String(200))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))Pythonuselist=False rend la relation unitaire (1:1).
Même principe que 1\:N sinon.
Relation Many-to-Many (N ↔ N)
Exemple : Un étudiant peut suivre plusieurs **cours**, et un cours peut avoir plusieurs étudiants.
Pour les cas complexes (ex: Étudiants et Cours), une table d’association est requise.
association_table = db.Table('association',
db.Column('student_id', db.Integer, db.ForeignKey('student.id')),
db.Column('course_id', db.Integer, db.ForeignKey('course.id'))
)
class Student(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
courses = db.relationship('Course', secondary=association_table, backref='students')
class Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100))
PythonUne table d’association (`association_table`) est nécessaire.
Utilisation de `secondary=` dans `db.relationship()` pour établir le lien.
Récapitulatif rapide :
| Relation | Côté A | Côté B |
|---|---|---|
| One-to-Many | db.relationship() | db.ForeignKey() |
| One-to-One | db.relationship(uselist=False) | db.ForeignKey() |
| Many-to-Many | db.relationship(secondary=…) | Table d’association avec db.Table() |
Grâce à **Flask-SQLAlchemy**, on manipule la base de données en Python, et avec Flask-Migrate, on gère les modifications de manière souple et sécurisée. 🚀