
Alembic est l'outil de migration de schéma officiel de SQLAlchemy. Il versionne l'évolution de votre base de données : chaque changement de modèle (ajout de colonne, nouvelle table) devient un script de migration horodaté, applicable avec upgrade et réversible avec downgrade. Vous faites ainsi évoluer une base en production sans la recréer ni perdre de données.
Ce guide fait suite à la première partie sur SQLAlchemy et se concentre sur la pratique : installer Alembic, l'initialiser dans un projet, générer des migrations automatiquement, les appliquer, les annuler et adopter les bons réflexes en équipe. Tout le cycle a été exécuté avec Alembic 1.18 sur une base SQLite.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Installer et initialiser Alembic dans un projet SQLAlchemy existant.
- Relier Alembic à vos modèles via
env.pyetalembic.ini. - Générer une migration automatiquement avec
--autogenerate. - Appliquer, annuler et inspecter les migrations (
upgrade,downgrade,history). - Éviter les pièges de l'autogénération et sécuriser les migrations en production.
Qu'est-ce qu'une migration et pourquoi Alembic
Section intitulée « Qu'est-ce qu'une migration et pourquoi Alembic »Une migration est un changement contrôlé du schéma d'une base : ajouter une colonne, créer une table, modifier un type. Le problème, c'est que Base.metadata.create_all() de SQLAlchemy ne crée que les tables absentes et n'altère jamais l'existant. Dès qu'une base contient des données réelles, recréer les tables n'est plus envisageable : il faut modifier le schéma en place, sans rien perdre.
Alembic répond exactement à ce besoin. Écrit par l'auteur de SQLAlchemy, il transforme chaque évolution en un script versionné que vous stockez dans Git à côté de votre code. Chaque environnement (poste de développement, préproduction, production) applique la même suite de migrations dans le même ordre, ce qui garantit des schémas identiques partout. En cas d'erreur, vous disposez d'un chemin de retour explicite.
Prérequis
Section intitulée « Prérequis »Avant de commencer, vous devez avoir :
- Python 3.10 ou supérieur et un environnement virtuel actif.
- Un projet SQLAlchemy fonctionnel, comme celui de la première partie.
- Des notions de modèles SQLAlchemy (
DeclarativeBase,Mapped).
Installer Alembic
Section intitulée « Installer Alembic »Alembic s'installe via pip. Il tire SQLAlchemy comme dépendance, mais autant l'installer explicitement :
pip install alembic sqlalchemyVérifiez la version. La sortie doit afficher un numéro 1.x :
alembic --version# alembic 1.18.5Le projet SQLAlchemy de départ
Section intitulée « Le projet SQLAlchemy de départ »Pour illustrer, partons d'un modèle minimal dans un fichier models.py, en syntaxe SQLAlchemy 2.0 :
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase): pass
class User(Base): __tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column(nullable=False)C'est ce modèle qu'Alembic va comparer à la base pour détecter les changements. Notez que nous ne créons pas la table à la main : c'est désormais le rôle des migrations.
Initialiser Alembic
Section intitulée « Initialiser Alembic »À la racine du projet, une seule commande met en place toute la structure :
alembic init alembicElle crée un dossier alembic/ et un fichier de configuration :
Répertoiremon_projet/
Répertoirealembic/
Répertoireversions/
- …
- env.py
- script.py.mako
- README
- alembic.ini
- models.py
Trois éléments comptent :
alembic.ini: la configuration principale, dont l'URL de la base.alembic/env.py: le script qui relie Alembic à vos modèles.alembic/versions/: le dossier où seront écrits les scripts de migration.
Configurer la connexion et les modèles
Section intitulée « Configurer la connexion et les modèles »Alembic ne connaît ni votre base ni vos modèles tant que vous ne les lui indiquez pas. Deux réglages sont nécessaires.
-
Indiquer l'URL de la base dans
alembic.ini. Repérez la lignesqlalchemy.urlet renseignez votre base SQLite :sqlalchemy.url = sqlite:///example.db -
Pointer Alembic vers vos modèles dans
alembic/env.py. Remplacez la lignetarget_metadata = Nonepar l'import de votreBase:from models import Basetarget_metadata = Base.metadata -
Activer le mode batch pour SQLite. Toujours dans
env.py, ajoutezrender_as_batch=Trueà l'appelcontext.configurede la fonctionrun_migrations_online:with connectable.connect() as connection:context.configure(connection=connection,target_metadata=target_metadata,render_as_batch=True,)
Générer la première migration
Section intitulée « Générer la première migration »Alembic sait maintenant comparer vos modèles à l'état réel de la base. La commande --autogenerate produit le script correspondant :
alembic revision --autogenerate -m "initial"La sortie confirme la détection de la table :
INFO [alembic.autogenerate.compare.tables] Detected added table 'users'Generating .../alembic/versions/7cac93520a2c_initial.py ... doneLe script généré dans alembic/versions/ décrit la création de la table. La fonction upgrade() applique le changement, downgrade() l'annule :
"""initial
Revision ID: 7cac93520a2cRevises:Create Date: 2026-07-01 19:34:52"""from alembic import opimport sqlalchemy as sa
revision = "7cac93520a2c"down_revision = None
def upgrade() -> None: op.create_table( "users", sa.Column("id", sa.Integer(), nullable=False), sa.Column("name", sa.String(), nullable=False), sa.PrimaryKeyConstraint("id"), )
def downgrade() -> None: op.drop_table("users")Appliquer la migration
Section intitulée « Appliquer la migration »Pour synchroniser la base avec le script, appliquez toutes les migrations jusqu'à la plus récente (head) :
alembic upgrade headINFO [alembic.runtime.migration] Running upgrade -> 7cac93520a2c, initialLa table users existe désormais, et Alembic a créé une table technique alembic_version qui mémorise la révision courante de la base.
Faire évoluer le schéma
Section intitulée « Faire évoluer le schéma »Le vrai intérêt d'Alembic apparaît quand le modèle change. Ajoutons un champ email à User :
class User(Base): __tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column(nullable=False) email: Mapped[str | None] = mapped_column(default=None) # nouveau champOn génère une nouvelle migration. Alembic détecte la colonne ajoutée :
alembic revision --autogenerate -m "add email"INFO [alembic.autogenerate.compare.tables] Detected added column 'users.email'Grâce au mode batch, le script utilise op.batch_alter_table, la forme compatible SQLite :
def upgrade() -> None: with op.batch_alter_table("users", schema=None) as batch_op: batch_op.add_column(sa.Column("email", sa.String(), nullable=True))
def downgrade() -> None: with op.batch_alter_table("users", schema=None) as batch_op: batch_op.drop_column("email")Un alembic upgrade head applique la modification. La colonne email est bien présente dans la table users.
Annuler une migration
Section intitulée « Annuler une migration »Si une migration pose problème, downgrade exécute sa fonction downgrade() pour revenir en arrière :
alembic downgrade -1 # annule la dernière migrationalembic downgrade 7cac93520a2c # revient à une révision précisealembic downgrade base # revient à une base videAprès un downgrade -1 sur notre exemple, la colonne email disparaît et la base retrouve son schéma précédent.
Inspecter l'état des migrations
Section intitulée « Inspecter l'état des migrations »Deux commandes donnent une vue claire de la situation :
alembic history # la chaîne complète des révisionsalembic current # la révision appliquée sur la baseLa sortie de history montre l'enchaînement des révisions, de la base jusqu'au head :
7cac93520a2c -> 5202795bdd97 (head), add email<base> -> 7cac93520a2c, initialBonnes pratiques et sécurité
Section intitulée « Bonnes pratiques et sécurité »Alembic est puissant, mais quelques réflexes évitent les incidents en équipe et en production.
- Relire chaque script autogénéré. L'autogénération est une aide, pas une vérité : elle rate les renommages et certains types. La revue est obligatoire.
- Ne jamais autogénérer contre la production. On génère et on relit en développement, on versionne dans Git, puis la production n'exécute que
alembic upgrade head, idéalement depuis la CI/CD. - Sauvegarder avant de migrer une base réelle. Une migration destructrice sans sauvegarde est irréversible.
- Sortir l'URL de production du fichier. Ne stockez pas l'URL de la base de production en clair dans
alembic.ini: lisez-la depuis une variable d'environnement dansenv.py(os.environ["DATABASE_URL"]). - Nommer les migrations clairement (
-m "add email field to users") et les committer avec le code applicatif correspondant, pour que chaque déploiement reste cohérent. - Résoudre les conflits de branches. Quand deux personnes créent une migration en parallèle, deux révisions partagent le même parent.
alembic mergecrée une révision de fusion pour réunir les deux branches.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »- Une migration modifie le schéma en place, sans recréer la base ni perdre les données.
- Alembic versionne ces changements en scripts stockés dans Git, appliqués à l'identique dans chaque environnement.
- Le cycle de base :
alembic init, configureralembic.inietenv.py, puisrevision --autogenerateetupgrade head. render_as_batch=Trueest indispensable sur SQLite pour modifier une colonne existante.- L'autogénération relit obligatoirement : un renommage mal détecté efface des données.
- En production, on applique seulement des migrations relues et testées, après sauvegarde ; le downgrade est rarement sûr.
alembic historyetalembic currentdonnent l'état des révisions.
FAQ : Alembic et les migrations
Section intitulée « FAQ : Alembic et les migrations »- appliquer pour mettre la base à jour (
upgrade), - annuler pour revenir en arrière (
downgrade).
ALTER TABLE à la main, au risque de perdre des données. Alembic rend ces changements traçables et reproductibles entre les environnements.pip install alembic
Puis initialisez la configuration à la racine du projet :alembic init alembic
Cette commande crée :alembic.ini: la configuration principale (dont l'URL de la base).alembic/env.py: le script qui relie Alembic à vos modèles.alembic/versions/: le dossier des scripts de migration.
sqlalchemy.url dans alembic.ini et pointer target_metadata vers le metadata de vos modèles dans env.py.env.py relié à vos modèles, générez une migration en comparant modèles et base :alembic revision --autogenerate -m "ajout du champ email"
Alembic compare votre Base.metadata à l'état réel de la base et écrit un script dans alembic/versions/. Appliquez-le ensuite :alembic upgrade head
Relisez toujours le script généré avant de l'appliquer : l'autogénération ne détecte pas tout (renommage de colonne vu comme suppression + ajout, valeurs par défaut serveur, certains changements de type). Un script non relu peut effacer des données.alembic upgrade head # applique tout jusqu'à la dernière migration
alembic upgrade +1 # applique la migration suivante seulement
alembic downgrade -1 # annule la dernière migration
alembic downgrade base # revient à une base vide
alembic history # affiche la chaîne des révisions
alembic current # version actuellement appliquée
En production, le downgrade est rarement sûr : annuler un add_column supprime la colonne et ses données. Préférez une nouvelle migration corrective plutôt qu'un downgrade sur une base réelle.ALTER TABLE : impossible de supprimer ou de modifier une colonne directement. Sans précaution, une migration de ce type échoue sur SQLite.Le mode batch d'Alembic contourne cette limite en recréant la table puis en recopiant les données. Activez-le dans alembic/env.py :context.configure(
connection=connection,
target_metadata=target_metadata,
render_as_batch=True,
)
Les migrations générées utilisent alors op.batch_alter_table(...). C'est indispensable si vous développez sur SQLite. PostgreSQL et MySQL n'en ont pas besoin, mais le laisser actif ne gêne pas.- En développement :
alembic revision --autogenerate, puis on relit le script. - On le versionne dans Git avec le code qui l'accompagne.
- En production : uniquement
alembic upgrade head, idéalement depuis la CI/CD et après une sauvegarde.
DROP destructeurs. La production n'exécute que des migrations déjà relues et testées. Ne stockez pas non plus l'URL de production en clair dans alembic.ini : lisez-la depuis une variable d'environnement.Pour aller plus loin
Section intitulée « Pour aller plus loin »- PostgreSQL : passer de SQLite à une base de production robuste.