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()

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

📌 Étapes pour utiliser `flask db migrate`

Voici comment fonctionne la migration avec Flask :
  1. Installation de Flask-Migrate
  2. Si ce n’est pas déjà fait, installe Flask-Migrate et SQLAlchemy : ```bash pip install flask-migrate flask-sqlalchemy ```
  3. Initialisation du projet
  4. Dans ton application Flask, tu dois avoir SQLAlchemy configuré :
    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)
    
  5. Initialisation de Flask-Migrate
  6. Crée un dossier de migration dans ton projet :
    flask db init
    
    👉 Cela génère un dossier `migrations/` contenant la configuration de Alembic.
  7. Création d'une migration
  8. Si tu ajoutes un nouveau modèle ou modifies un champ, Flask-Migrate va générer une migration :
    flask db migrate -m "Ajout du modèle Task"
    
    👉 Cette commande crée un fichier dans `migrations/versions/` qui contient les instructions de modification du schéma.
  9. Application de la migration
  10. Une fois la migration créée, applique-la à la base de données :
    flask db upgrade
    
    👉 Cela exécute le script de migration et met à jour la base.
  11. Gérer les modifications du modèle
  12. Si tu modifies 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 colonne
    
    Exécute les migrations :
    flask db migrate -m "Ajout de due_date à Task"
    flask db upgrade
    
    Annuler la dernière migration (si besoin) :
       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 :

  1. Modifier le modèle Python :
  2.    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 colonne
       
  3. Générer une nouvelle migration :
  4.    flask db migrate -m "Ajout de la colonne age à User"
      
  5. Appliquer la migration :
  6.    flask db upgrade
      

6. 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'))
  • db.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'))
  • uselist=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))
  • Une 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. 🚀