Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentesRévision précédente | |||
| dev:python:flask:extensions:flask_migrate [2025/09/10 13:07] – supprimée - modification externe (Date inconnue) 127.0.0.1 | dev:python:flask:extensions:flask_migrate [2025/09/10 13:07] (Version actuelle) – ↷ Page déplacée de dev:python:flask:flask_migrate à dev:python:flask:extensions:flask_migrate yoann | ||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| + | {{tag> | ||
| + | |||
| + | ====== Flask : Gérer les migrations avec l' | ||
| + | |||
| + | |||
| + | ===== Présentation ===== | ||
| + | |||
| + | Flask-Migrate s' | ||
| + | |||
| + | |||
| + | Pour illustrer le fonctionnement de l' | ||
| + | |||
| + | |||
| + | ===== Installation ===== | ||
| + | |||
| + | Création d'un environnement virtuel Python pour le projet Flask et installation des packages via pip : | ||
| + | |||
| + | <code bash> | ||
| + | #création d'un répertoire dédié à l' | ||
| + | mkdir tuto-migrate && cd tuto-migrate | ||
| + | |||
| + | # création /activation de l' | ||
| + | python3 -m venv .venv | ||
| + | source .venv/ | ||
| + | |||
| + | # Installation de Flask et de l' | ||
| + | pip install Flask Flask-SQLAlchemy Flask-Migrate | ||
| + | </ | ||
| + | |||
| + | |||
| + | ===== Création de l' | ||
| + | |||
| + | Création de l' | ||
| + | |||
| + | <code python app.py> | ||
| + | from flask import Flask | ||
| + | from flask_sqlalchemy import SQLAlchemy | ||
| + | from flask_migrate import Migrate | ||
| + | |||
| + | app = Flask(__name__) | ||
| + | app.config[" | ||
| + | db = SQLAlchemy(app) | ||
| + | migrate = Migrate(app, | ||
| + | |||
| + | |||
| + | class Product(db.Model): | ||
| + | __tablename__ = " | ||
| + | id = db.Column(db.Integer, | ||
| + | name = db.Column(db.String(50)) | ||
| + | |||
| + | def __repr__(self): | ||
| + | return f"< | ||
| + | |||
| + | |||
| + | @app.route("/" | ||
| + | def index(): | ||
| + | return "< | ||
| + | </ | ||
| + | |||
| + | Le module Python app.py définit la classe '' | ||
| + | |||
| + | Pour tester rapidement le code : | ||
| + | |||
| + | <code bash> | ||
| + | # Afficher les routes existantes | ||
| + | flask routes | ||
| + | |||
| + | # Pour tester la création d'une instance de Product | ||
| + | flask shell | ||
| + | </ | ||
| + | |||
| + | <code python> | ||
| + | >>> | ||
| + | >>> | ||
| + | <Product id=None name=' | ||
| + | >>> | ||
| + | </ | ||
| + | |||
| + | ===== Initialisation de Flask-Migrate ===== | ||
| + | |||
| + | Les commandes proposées par l' | ||
| + | |||
| + | <code bash> | ||
| + | # Création de l' | ||
| + | flask db init | ||
| + | |||
| + | # Création du premier script | ||
| + | flask db migrate -m " | ||
| + | </ | ||
| + | |||
| + | La commande retourne quelques messages de la forme : | ||
| + | < | ||
| + | INFO [alembic.runtime.migration] Context impl SQLiteImpl. | ||
| + | INFO [alembic.runtime.migration] Will assume non-transactional DDL. | ||
| + | INFO [alembic.autogenerate.compare] Detected added table ' | ||
| + | Generating / | ||
| + | </ | ||
| + | |||
| + | L' | ||
| + | |||
| + | < | ||
| + | Le dossier '' | ||
| + | </ | ||
| + | |||
| + | A ce stade, le script existe mais n'a pas été appliqué. Il est intéressant de le relire : | ||
| + | |||
| + | <code bash> | ||
| + | less migrations/ | ||
| + | </ | ||
| + | |||
| + | On peut y voir deux fonctions : '' | ||
| + | |||
| + | <note warning> | ||
| + | Les scripts de migration sont générés automatiquement et peuvent comporter des erreurs ou des imperfections en fonction de la complexité et de la nature des changements apportés au modèle de données. Il est donc recommandé de vérifier le code généré. | ||
| + | </ | ||
| + | |||
| + | Si on affiche le contenu de la base on peut vérifier qu' | ||
| + | <code bash> | ||
| + | sqlite3 instance/ | ||
| + | alembic_version | ||
| + | |||
| + | # Une seule table existe nommée alembic_version, | ||
| + | sqlite3 instance/ | ||
| + | </ | ||
| + | |||
| + | Pour appliquer les modifications décrites dans le script de migration à la base de données du projet : | ||
| + | |||
| + | <code bash> | ||
| + | flask db upgrade | ||
| + | </ | ||
| + | |||
| + | La commande retourne quelques messages du type : | ||
| + | < | ||
| + | INFO [alembic.runtime.migration] Context impl SQLiteImpl. | ||
| + | INFO [alembic.runtime.migration] Will assume non-transactional DDL. | ||
| + | INFO [alembic.runtime.migration] Running upgrade | ||
| + | </ | ||
| + | |||
| + | Si on inspecte à nouveau la base, on peut voir que la table '' | ||
| + | |||
| + | <code bash> | ||
| + | # Lister les tables existantes | ||
| + | sqlite3 instance/ | ||
| + | alembic_version | ||
| + | |||
| + | # Schéma des tables | ||
| + | sqlite3 instance/ | ||
| + | CREATE TABLE alembic_version ( | ||
| + | version_num VARCHAR(32) NOT NULL, | ||
| + | CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num) | ||
| + | ); | ||
| + | CREATE TABLE products ( | ||
| + | id INTEGER NOT NULL, | ||
| + | name VARCHAR(50), | ||
| + | PRIMARY KEY (id) | ||
| + | ); | ||
| + | |||
| + | # Afficher le contenu de la table alembic_version | ||
| + | sqlite3 instance/ | ||
| + | a09681708867 | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | Le premier script de migration équivaut à l' | ||
| + | </ | ||
| + | |||
| + | <note warning> | ||
| + | Lorsqu' | ||
| + | </ | ||
| + | |||
| + | A chaque modification du modèle de données, il faudra répéter les opérations '' | ||
| + | |||
| + | |||
| + | ===== Peuplement de la base de données ===== | ||
| + | |||
| + | Pour illustrer le bon fonctionnement de Flask-Migrate nous allons : | ||
| + | - Introduire des données dans la base ; | ||
| + | - Modifier le modèle de données ; | ||
| + | - Créer et appliquer une migration afin de vérifier que la base existante peut utiliser notre nouveau modèle de données. | ||
| + | |||
| + | Pour ajouter des données dans la base, on utilise '' | ||
| + | <code python> | ||
| + | >>> | ||
| + | >>> | ||
| + | >>> | ||
| + | >>> | ||
| + | >>> | ||
| + | >>> | ||
| + | >>> | ||
| + | >>> | ||
| + | >>> | ||
| + | [< | ||
| + | >>> | ||
| + | </ | ||
| + | |||
| + | ===== Modification du modèle de données ===== | ||
| + | |||
| + | |||
| + | On modifie la classe '' | ||
| + | |||
| + | <code python app.py> | ||
| + | |||
| + | </ | ||
| + | |||
| + | On regénère un script de migration : | ||
| + | |||
| + | <code bash> | ||
| + | flask db migrate -m "Ajout attribut price" | ||
| + | </ | ||
| + | |||
| + | La commande retourne des messages indiquant que des modifications ont été identifiées et qu'un nouveau script est produit : | ||
| + | < | ||
| + | INFO [alembic.runtime.migration] Context impl SQLiteImpl. | ||
| + | INFO [alembic.runtime.migration] Will assume non-transactional DDL. | ||
| + | INFO [alembic.autogenerate.compare] Detected added column ' | ||
| + | Generating / | ||
| + | </ | ||
| + | |||
| + | |||
| + | ===== Mise à niveau de la base de données ===== | ||
| + | |||
| + | On peut consulter ce nouveau script puis l' | ||
| + | <code bash> | ||
| + | less migrations/ | ||
| + | flask db upgrade | ||
| + | </ | ||
| + | |||
| + | Pour vérifier que le nouveau modèle s' | ||
| + | |||
| + | <code python> | ||
| + | >>> | ||
| + | [< | ||
| + | >>> | ||
| + | </ | ||
| + | |||
| + | Même si aucune valeur n'est renseignée, | ||
| + | |||
| + | |||
| + | ===== Rétrogradation de la base de données ===== | ||
| + | |||
| + | Version actuelle de la base : | ||
| + | <code bash> | ||
| + | sqlite3 instance/ | ||
| + | 7d38ed81182c | ||
| + | |||
| + | sqlite3 instance/ | ||
| + | CREATE TABLE products ( | ||
| + | id INTEGER NOT NULL, | ||
| + | name VARCHAR(50), | ||
| + | PRIMARY KEY (id) | ||
| + | ); | ||
| + | </ | ||
| + | |||
| + | Pour rétrograder la base à la version précédente : | ||
| + | <code bash> | ||
| + | flask db dowgrade | ||
| + | </ | ||
| + | |||
| + | Etat de la base : | ||
| + | <code bash> | ||
| + | sqlite3 instance/ | ||
| + | a09681708867 | ||
| + | |||
| + | sqlite3 instance/ | ||
| + | CREATE TABLE IF NOT EXISTS " | ||
| + | id INTEGER NOT NULL, | ||
| + | name VARCHAR(50), | ||
| + | PRIMARY KEY (id) | ||
| + | ); | ||
| + | |||
| + | </ | ||
| + | |||
| + | On peut voir que la version a bien été modifiée et que la table ne comporte plus de colonne '' | ||
| + | |||
| + | Mais notre modèle de données n'est plus en adéquation avec le schéma de la base de données : son utilisation provoquera une erreur : | ||
| + | |||
| + | <code python> | ||
| + | >>> | ||
| + | >>> | ||
| + | >>> | ||
| + | >>> | ||
| + | >>> | ||
| + | >>> | ||
| + | </ | ||
| + | |||
| + | Le commit retournera un ensemble de messages de la forme : | ||
| + | |||
| + | < | ||
| + | Traceback (most recent call last): | ||
| + | File "/ | ||
| + | self.dialect.do_execute( | ||
| + | File "/ | ||
| + | cursor.execute(statement, | ||
| + | sqlite3.OperationalError: | ||
| + | |||
| + | ... | ||
| + | </ | ||
| + | |||
| + | Il faudra retirer l' | ||
| + | |||
| + | |||
| + | ===== Renommer les commandes de l' | ||
| + | |||
| + | Le groupe de commandes par défaut de Flask-Migrate est nommé " | ||
| + | |||
| + | <code python app.py> | ||
| + | migrate = Migrate(app, | ||
| + | </ | ||
| + | |||
| + | Au sein du projet, on peut à présent appeler les commandes de Flask-Migrate : | ||
| + | <code bash> | ||
| + | flask db-tools show | ||
| + | flask db-tools --help | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | ===== Résumé ===== | ||
| + | |||
| + | On a créé une application Flask minimale et utilisé l' | ||
| + | |||
| + | En général on peut suivre les étapes suivantes lors du developpement d'une application Flask : | ||
| + | - Modifier les modèles ; | ||
| + | - Générer un script de migration via la commande '' | ||
| + | - Vérifier le script généré et le compléter si nécessaire ; | ||
| + | - Appliquer les changements sur la base de données via la commande '' | ||
| + | |||
| + | La documentation complète de l' | ||
| + | |||
| + | |||
| + | ===== Références ===== | ||
| + | |||
| + | * [[https:// | ||
| + | * [[https:// | ||
| + | * [[https:// | ||