Aller au contenu

SqlAlchemy, l'ORM Python - Partie 2 : Alembic

Mise à jour :

logo SQLAlchemy

Dans la première partie de ce tutoriel, nous avons vu comment utiliser SqlAlchemy pour manipuler des bases de données relationnelles en Python. Dans cette deuxième partie, nous allons voir comment utiliser Alembic pour gérer les migrations de schéma.

Quand on travaille sur des projets nécessitant des bases de données relationnelles, la gestion des migrations devient rapidement un casse-tête. Vous avez une base en production, vos développeurs ajoutent de nouvelles colonnes, suppriment des tables ou modifient des types de données… et là, tout peut déraper. C’est là qu’Alembic entre en scène.

Alembic, c’est un outil dédié aux migrations de bases de données, pensé pour les développeurs qui utilisent SQLAlchemy. Il permet de gérer les changements de schéma de manière structurée, sans risquer de casser l’existant. En clair, il est là pour vous éviter des nuits blanches en production !

Installation et configuration d’Alembic

Pour commencer avec Alembic, il faut l’installer et s’assurer que votre environnement est prêt à l’utiliser avec une base de données, par exemple SQLite. Voici les étapes à suivre pour une installation simple et rapide.

Avant d’installer Alembic, assurez-vous d’avoir :

  • Python (version 3.7 ou supérieure) installé sur votre machine.
  • Une base de données SQLite prête à être utilisée (même un fichier vide fera l’affaire).
  • L’outil pip pour installer les dépendances Python.

Vous pouvez vérifier ces pré-requis avec les commandes suivantes :

Terminal window
python --version
pip --version

Alembic peut être installé via pip. Exécutez simplement la commande suivante :

Terminal window
pip install alembic

Pour vérifier que l’installation a réussi, utilisez la commande suivante pour afficher la version installée :

Terminal window
alembic --version

Si tout est correct, vous verrez un numéro de version (par exemple, 1.10.0).

Créer un projet avec SQLAlchemy et SQLite

Pour que Alembic soit utile, vous devez avoir un projet utilisant SQLAlchemy avec une base de données. Voici comment créer un environnement minimal avec SQLite :

  1. Installer SQLAlchemy : Si ce n’est pas déjà fait, installez SQLAlchemy avec la commande suivante :

    Terminal window
    pip install sqlalchemy
  2. Créer un fichier de base pour SQLAlchemy : Créez un fichier Python, par exemple app.py, avec le code suivant pour initialiser une base SQLite :

    from sqlalchemy import create_engine, Column, Integer, String, MetaData
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import sessionmaker
    # Création de la base SQLite
    engine = create_engine("sqlite:///example.db")
    Base = declarative_base()
    # Exemple de modèle
    class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    # Création des tables
    Base.metadata.create_all(engine)
    # Session
    Session = sessionmaker(bind=engine)
    session = Session()
    print("Base de données SQLite prête !")

    Ce script crée une base SQLite nommée example.db et une table simple nommée users.

  3. Exécuter le script : Lancez votre script pour vérifier que tout fonctionne correctement :

    Terminal window
    python app.py

    Après l’exécution, vous devriez voir un fichier example.db dans le répertoire courant.

Intégration d’Alembic au projet

Une fois SQLAlchemy installé et fonctionnel, Alembic peut être utilisé pour gérer les migrations de votre base de données. Nous couvrirons cette configuration dans le chapitre suivant, mais pour l’instant, vous êtes prêt à initialiser Alembic dans votre projet.

Terminal window
alembic init alembic

Cela crée un dossier alembic/ et un fichier alembic.ini dans votre projet. Voici ce que contient chaque élément important :

  • alembic.ini : le fichier de configuration principal où vous indiquerez les détails de votre base de données.
  • Dossier alembic/ :
    • env.py : le script qui configure les connexions à votre base et contrôle les migrations.
    • versions/ : le répertoire où seront stockés vos scripts de migration.

Configurer la connexion à SQLite

Par défaut, Alembic ne sait pas encore où trouver votre base de données. Il faut donc modifier le fichier alembic.ini pour lui indiquer l’URL de connexion de votre base SQLite.

  1. Ouvrez le fichier alembic.ini.
  2. Localisez la ligne contenant sqlalchemy.url et remplacez-la par l’URL de votre base SQLite. Par exemple :
sqlalchemy.url = sqlite:///example.db

Cela indique à Alembic d’utiliser le fichier example.db comme base de données.

Le fichier env.py est important car il contient la configuration des migrations et des métadonnées de votre projet. Voici les modifications à effectuer pour intégrer vos modèles SQLAlchemy.

  1. Ouvrez alembic/env.py.
  2. Localisez la section suivante et modifiez-la pour inclure vos modèles SQLAlchemy :
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
# Importez vos modèles ici
from app import Base
# Configuration de l'environnement Alembic
config = context.config
fileConfig(config.config_file_name)
# Indiquez les métadonnées de vos modèles
target_metadata = Base.metadata

Avec ces modifications, Alembic sait désormais où trouver vos modèles et peut comparer leurs schémas avec la base de données.

Créer une première migration

Maintenant que Alembic est configuré, vous pouvez créer une première migration pour capturer l’état actuel de vos modèles SQLAlchemy.

  1. Générez un script de migration avec la commande suivante :
Terminal window
alembic revision --autogenerate -m "Initial migration"
  1. Cette commande :
    • Compare vos modèles SQLAlchemy (via Base.metadata) avec la base de données actuelle.
    • Génère un script de migration dans le répertoire alembic/versions/.

Le script ressemblera à ceci :

"""Initial migration"""
revision = '1234567890ab'
down_revision = None
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
def upgrade():
op.create_table(
'users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
def downgrade():
op.drop_table('users')

Appliquer la première migration

Pour synchroniser votre base SQLite avec le script de migration, exécutez la commande suivante :

Terminal window
alembic upgrade head

Cette commande applique toutes les migrations disponibles jusqu’à la dernière version (head).

Vous pouvez vérifier que la table users a bien été créée en utilisant un outil comme sqlite3 ou en écrivant un script Python simple pour interroger la base.

Vérifier l’historique des migrations

Pour afficher l’historique des migrations appliquées, utilisez la commande suivante :

Terminal window
alembic history

Cela vous montrera toutes les révisions créées, avec leurs identifiants uniques (revision).

Création de nouvelles migrations

Chaque fois que vous modifiez vos modèles SQLAlchemy, vous devez créer une nouvelle migration pour synchroniser ces changements avec votre base de données.

  1. Modifier les modèles : Par exemple, ajoutons un nouvel attribut email à notre modèle User dans le fichier app.py :

    class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    email = Column(String, nullable=True) # Nouveau champ
  2. Créer une nouvelle révision : Générez un script de migration avec la commande suivante :

    Terminal window
    alembic revision --autogenerate -m "Add email field to users"

    Cette commande :

    • Compare les métadonnées SQLAlchemy (vos modèles) avec le schéma actuel de la base.
    • Génère un fichier de migration dans le dossier alembic/versions/.
  3. Examiner le script généré : Voici à quoi ressemble le script généré automatiquement dans alembic/versions/ :

    """Add email field to users"""
    revision = 'abcdef123456'
    down_revision = '1234567890ab'
    branch_labels = None
    depends_on = None
    from alembic import op
    import sqlalchemy as sa
    def upgrade():
    op.add_column('users', sa.Column('email', sa.String(), nullable=True))
    def downgrade():
    op.drop_column('users', 'email')

    Le bloc upgrade décrit ce qu’Alembic doit faire pour appliquer la migration, et le bloc downgrade décrit comment annuler ces changements.

Appliquer les migrations

Après avoir validé le script, appliquez-le à la base de données pour synchroniser le schéma.

  1. Appliquer toutes les migrations non exécutées : Utilisez la commande suivante pour appliquer les migrations :

    Terminal window
    alembic upgrade head

    Cette commande met à jour votre base jusqu’à la dernière migration disponible (head).

  2. Vérifier la base de données : Vous pouvez confirmer que la colonne email a été ajoutée en utilisant un outil comme sqlite3 ou en interrogeant la base avec SQLAlchemy.

Annuler une migration

Il peut arriver que vous deviez annuler une migration si elle contient une erreur ou si elle n’est plus nécessaire.

  1. Revenir à une migration précédente : Pour annuler la dernière migration, utilisez la commande suivante :

    Terminal window
    alembic downgrade -1

    Cette commande exécute la fonction downgrade de la migration la plus récente.

  2. Revenir à une version spécifique : Vous pouvez également revenir à une migration particulière en utilisant son identifiant (revision) :

    Terminal window
    alembic downgrade abcdef123456

Gérer les conflits de migrations

Dans un projet collaboratif, plusieurs développeurs peuvent créer des migrations en parallèle, ce qui peut entraîner des conflits.

  1. Conflit courant : Un conflit se produit généralement lorsque deux migrations ont la même down_revision.

  2. Résolution :

    • Modifiez manuellement les fichiers de migration pour ajuster les down_revision afin de refléter correctement l’ordre des migrations.
    • Assurez-vous de bien tester les migrations après les avoir fusionnées.

Bonnes pratiques pour gérer les migrations

  • Toujours utiliser --autogenerate : Cela réduit les risques d’erreurs humaines en générant automatiquement les changements.
  • Examiner chaque script généré : Vérifiez que le script correspond exactement aux changements souhaités.
  • Nommer les migrations de manière descriptive : Utilisez des descriptions claires, comme "Add email field to users".
  • Synchroniser avec l’équipe : Dans un projet collaboratif, assurez-vous que tous les membres sont à jour avec les migrations avant de travailler sur de nouveaux modèles.

Conclusion

Ce que j’apprécie le plus avec Alembic, c’est sa capacité à évoluer avec vos projets. Au fil du temps, vous verrez que l’adopter, c’est éviter des heures de travail manuel, des erreurs critiques en production, et des conflits interminables entre développeurs.

Alors, lancez-vous ! Testez, migrez, et intégrez Alembic dans vos projets. Une chose est sûre : vos bases de données ne s’en porteront que mieux.