Comment effectuer des migrations Flask-SQLAlchemy à l'aide de Flask-Migrate
L'auteur a sélectionné Apache Software Foundation pour recevoir un don dans le cadre du programme Write for DOnations.
Introduction
Flask est un framework Web Python léger qui fournit des outils et des fonctionnalités précieux pour créer des applications Web dans le langage Python. SQLAlchemy est une boîte à outils SQL offrant un accès efficace et performant aux bases de données relationnelles. Il fournit des moyens d'interagir avec plusieurs moteurs de bases de données, tels que SQLite, MySQL et PostgreSQL. Il vous donne accès aux fonctionnalités SQL de la base de données. Il vous donne également un Object Relational Mapper (ORM), qui vous permet d'effectuer des requêtes et de gérer des données à l'aide d'objets et de méthodes Python simples. Flask-SQLAlchemy est une extension Flask qui facilite l'utilisation de SQLAlchemy avec Flask, en vous fournissant des outils et des techniques pour interagir avec votre base de données dans vos applications Flask via SQLAlchemy.
Flask-Migrate est une extension Flask basée sur la bibliothèque Alembic, permettant de gérer les migrations de bases de données.
La migration de bases de données consiste à transférer des données entre différents schémas de bases de données sans aucune perte de données. Il est couramment utilisé pour mettre à niveau une base de données, modifier son schéma en ajoutant de nouvelles colonnes ou relations de table et garantir une transition fluide avec un temps d'arrêt minimal et aucune perte de données.
Par exemple, si vous disposez d'une table appelée Produit
avec une liste de noms de produits et que vous souhaitez ajouter une colonne de prix à cette table, vous pouvez utiliser la migration de base de données pour ajouter la colonne de prix sans perdre les données de produit existantes. .
Dans ce didacticiel, vous utiliserez Flask-Migrate avec Flask-SQLAlchemy pour effectuer des migrations de schémas de base de données afin de modifier vos tables et de conserver les données.
Conditions préalables
Pour un environnement de programmation Python 3 local, suivez le didacticiel correspondant à votre distribution dans Comment installer et configurer un environnement de programmation local pour la série Python 3. Dans ce tutoriel, nous appellerons notre répertoire de projet
flask_app
.Une compréhension des concepts de base de Flask, tels que les itinéraires, les fonctions d'affichage et les modèles. Si vous n'êtes pas familier avec Flask, consultez Comment créer votre première application Web à l'aide de Flask et Python et Comment utiliser des modèles dans une application Flask.
Une compréhension des concepts HTML de base. Consultez notre série de didacticiels Comment créer un site Web avec HTML.
Comprendre les concepts de base de Flask-SQLAlchemy, tels que la configuration d'une base de données, la création de modèles de base de données et l'insertion de données dans la base de données. Voir Comment utiliser Flask-SQLAlchemy pour interagir avec des bases de données dans une application Flask pour obtenir des connaissances de base.
Étape 01 - Installation des packages Python requis
Au cours de cette étape, vous installerez les packages nécessaires pour votre application.
Dans votre répertoire flask_app
, activez votre environnement virtuel :
source <my_env>/bin/activate
Une fois votre environnement virtuel activé, utilisez pip
pour installer Flask, Flask-SQLAlchemy et Flask-Migrate :
pip install Flask Flask-SQLAlchemy Flask-Migrate
Une fois l'installation terminée, la sortie imprimera une ligne semblable à la suivante :
Successfully installed Flask-3.0.0 Flask-Migrate-4.0.5 Flask-SQLAlchemy-3.1.1 Jinja2-3.1.2 Mako-1.3.0 MarkupSafe-2.1.3 Werkzeug-3.0.1 alembic-1.12.1 blinker-1.7.0 click-8.1.7 greenlet-3.0.1 itsdangerous-2.1.2 sqlalchemy-2.0.23 typing-extensions-4.8.0
Une fois les packages Python requis installés, vous configurerez ensuite un exemple de base de données et de modèle.
Étape 02 - Configuration d'un exemple de base de données et de modèle
Dans cette étape, vous configurerez votre application Flask et une base de données Flask-SQLAlchemy avec un modèle représentant une table de produits dans laquelle vous stockerez les produits de votre boutique.
Dans votre répertoire flask_app
, ouvrez un nouveau fichier appelé app.py.
Il contiendra le code principal de votre application Flask :
nano app.py
Ajoutez-y le code suivant :
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db"
db = SQLAlchemy(app)
class Product(db.Model):
__tablename__ = "products"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
def __repr__(self):
return f"<Product {self.name}>"
@app.route("/")
def index():
return "<h1>Hello, World!</h1>"
Enregistrez et fermez le fichier.
Vous importez ici la classe Flask
et SQLAlchemy
à partir des modules flask
et flask_sqlalchemy
. Ensuite, vous créez une instance d'application Flask appelée app
.
Ensuite, vous définissez l'URI de la base de données SQLite dans la configuration de l'application ; cela spécifie le nom du fichier de base de données comme app.db
. Ce fichier de base de données sera créé dans un nouveau dossier appelé instance
qui sera généré par Flask dans le répertoire principal flask_app
.
Dans db=SQLAlchemy(app),
vous créez une instance SQLAlchemy, db,
en contournant l'application Flask comme argument.
Ensuite, vous créez une classe Product
qui hérite de db.Model
, représentant une table de base de données nommée « produits ». Dans le tableau, vous définissez une colonne id
pour l'ID du produit et une colonne name
pour le nom du produit.
La fonction spéciale repr vous permet de donner à chaque objet une représentation sous forme de chaîne pour le reconnaître à des fins de débogage.
Enfin, vous créez une route ('/')
qui renvoie une simple réponse HTML ("
lorsque l'URL racine est accessible.Hello, World!
")
Exécutez la commande suivante pour tester que l'application est correctement configurée. Cela exécute l'application Flask app
sur un serveur de développement avec le débogage activé :
flask --app app run --debug
Une fois que vous aurez exécuté cette commande, vous recevrez le résultat suivant :
* Serving Flask app 'app'
* Debug mode: on
WARNING: This is a development server. Please do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: 633-501-683
Cela vous donne des informations sur votre application pendant son exécution.
Avec le serveur de développement en cours d'exécution, visitez l'URL suivante à l'aide de votre navigateur :
http://127.0.0.1:5000/
Vous obtiendrez un en-tête <h1>
, « Bonjour le monde ! ». Cela confirme que l'application est correctement configurée. Vous pouvez maintenant passer à l'étape suivante et ajouter Flask-Migrate à votre application.
Étape 03 - Ajout de Flask-Migrate à votre application
Dans cette étape, vous allez modifier votre fichier d'application app.py
pour ajouter Flask-Migrate et l'utiliser pour gérer votre base de données Flask-SQLAlchemy.
Tout d'abord, ouvrez app.py
pour modification :
nano app.py
Modifiez le fichier pour que tout ce qui se trouve au-dessus de la déclaration de la classe Product
soit comme suit :
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)
migrate = Migrate(app, db)
class Product(db.Model):
Enregistrez et fermez le fichier.
Vous importez la classe Migrate
à partir du package flask_migrate
.
Après la déclaration db
, vous utilisez la classe Migrate
pour lancer une instance de migration appelée migrate,
en la transmettant à l'app Flask
. et l'instance de base de données db
.
Flask-Migrate fournit une aide à la commande flask db
pour gérer votre base de données.
Pour terminer la configuration de Flask-Migrate et ajouter la prise en charge à votre projet actuel, utilisez la commande suivante dans votre répertoire flask_app
:
flask db init
Vous recevrez un résultat similaire à celui-ci :
[secondary_label Output]
Creating directory 'flask_app/migrations' ... done
Creating directory 'flask_app/migrations/versions' ... done
Generating flask_app/migrations/README ... done
Generating flask_app/migrations/script.py.mako ... done.
Generating flask_app/migrations/env.py ... done
Generating flask_app/migrations/alembic.ini ... done
Please edit the configuration/connection/logging settings in
'flask_app/migrations/alembic.ini' before proceeding.
Cela crée un nouveau répertoire migrations
dans votre dossier flask_app
, où tous les scripts de migration qui gèrent vos migrations seront stockés.
Si vous êtes familier avec Alembic et souhaitez ajouter des configurations avancées à votre système de migration de bases de données, vous pouvez modifier le fichier migrations/alembic.ini
généré. Pour nos besoins, nous le laisserons tel quel.
Remarque : Le répertoire migrations
contient des fichiers qui gèrent les migrations de bases de données de votre application. Ils doivent être ajoutés à votre référentiel de contrôle de version avec le reste du code de votre application.
Avec Flask-Migrate
connecté à votre application, vous effectuerez une première migration de base de données. L'utilisation de la classe ' Product ' créera la table products que vous avez déclarée précédemment.
Création de la table des produits à l'aide d'un script de migration
Vous allez maintenant effectuer votre première migration en créant la table Produits
de votre base de données.
Dans votre répertoire flask_app
, exécutez la commande suivante. Cette commande flask db migrate
détecte toutes les nouvelles tables ou modifications que vous effectuez sur vos modèles de base de données Flask-SQLAlchemy. Le flag -m
vous permet de spécifier un court message décrivant la modification que vous avez effectuée :
flask db migrate -m "initial migration"
Vous recevrez le résultat suivant :
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table 'products'
Generating
flask_app/migrations/versions/b9198aca3963_initial_migration.py
...done
Comme notre base de données n'a pas encore été créée, cette sortie vous informe qu'une nouvelle table appelée products
a été détectée, ainsi qu'un script de migration appelé b9198aca3963 initial_migration.py
a été créé dans un répertoire appelé versions,
où sont stockées toutes les différentes versions de migration.
La partie b9198aca3963
dans le nom de fichier du script de migration est un identifiant aléatoire généré pour identifier différentes migrations afin qu'il soit différent pour vous.
Pour comprendre le fonctionnement d'un script de migration, ouvrez le vôtre avec la commande suivante. Assurez-vous de remplacer b9198aca3963
par l'ID qui a été généré pour vous et que vous vous trouvez dans votre répertoire racine flask_app
:
nano migrations/versions/b9198aca3963_initial_migration.py
Outre les importations internes et une configuration, ce script de migration initial aura deux fonctions principales similaires aux suivantes :
def upgrade():
# ### commands auto-generated by Alembic - please adjust! ###
op.create_table('products',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=50), nullable=True),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto-generated by Alembic - please adjust! ###
op.drop_table('products')
# ### end Alembic commands ###
La fonction
upgrade()
modifie la base de données en fonction des modifications détectées par la commandeflask db migrate
. Dans ce cas, une nouvelle table appeléeproducts
a été détectée, donc la fonctionupgrade()
crée la nouvelle table avec les colonnes spécifiées dansProduct()
modèle de base de données que vous avez déclaré dans le fichierapp.py
.La fonction
downgrade()
supprime les modifications et restaure l'état de la base de données telle qu'elle était avant la mise à niveau. Dans cet exemple, l'état précédent est restauré en supprimant la tableproducts
qui sera créée par la fonctionupgrade()
.
Remarque : Gardez à l'esprit que les scripts de migration sont du code généré automatiquement, qui peut comporter des erreurs en fonction de la complexité des modifications que vous effectuez avant la migration. Par conséquent, vous devez lire et ajuster attentivement vos scripts de migration pour garantir leur exactitude et leur bonne exécution.
Une fois le script de migration prêt, vous pouvez maintenant l'utiliser pour effectuer une première mise à niveau. Cela créera le fichier de base de données app.db
et la table products
. Pour ce faire, exécutez la commande suivante dans votre répertoire flask_app
:
flask db upgrade
Le résultat sera similaire à ce qui suit :
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> b9198aca3963, initial migration
Cela vous informe que la mise à niveau a été exécutée avec succès.
Un nouveau fichier de base de données app.db
sera ajouté à votre dossier instance
dans votre répertoire flask_app
.
Remarque : La première migration équivaut à utiliser db.create_all()
dans le shell Flask.
Si vous introduisez Flask-Migrate dans un projet existant avec une base de données existante, alors la mise à niveau de la base de données Flask
échouera car un fichier de base de données existe déjà. Dans ce cas, utilisez flask db stamp
pour marquer la base de données comme mise à niveau au lieu de flask db update.
Une fois la base de données et la table products
créées, vous pouvez maintenant ajouter quelques produits à votre base de données. Vous ferez cela ensuite.
Remplir la base de données
Vous allez maintenant utiliser le shell Flask pour insérer quelques éléments dans la table products
.
Une fois votre environnement virtuel activé, exécutez la commande suivante pour accéder au shell Flask :
flask shell
Dans le shell interactif, exécutez le code suivant :
from app import db, Product
apple = Product(name="Apple")
orange = Product(name="Orange")
banana = Product(name="Banana")
db.session.add_all([apple, orange, banana])
db.session.commit()
Ce code effectue les opérations suivantes :
- Importe l'objet
db
et le modèleProduct
à partir du fichierapp.py
. - Crée trois instances de la classe
Product()
en passant un nom pour chaque produit. - Ajoute les nouveaux produits dans la session de base de données à l'aide de la méthode
db.session.add_all()
. - Applique les modifications à la base de données à l'aide de la méthode
db.session.commit()
.
Pour en savoir plus sur l'utilisation de Flask-SQLAlchemy, consultez Comment utiliser Flask-SQLAlchemy pour interagir avec des bases de données dans une application Flask.
Quittez le shell Flask :
exit()
Pour vous assurer que les éléments du produit ont été ajoutés à la base de données, relancez le shell Flask avec une nouvelle mémoire :
flask shell
Exécutez ensuite le code suivant pour parcourir les éléments de la table products :
from app import db, Product
for p in Product.query.all():
print(p.name, p.id)
Ici, vous importez l'objet db
et le modèle Product
; puis vous utilisez une boucle for
sur le résultat de la méthode query.all()
pour accéder à chaque élément de la table products et imprimer le nom et l'ID de chaque élément.
Le résultat sera le suivant :
Apple 1
Orange 2
Banana 3
Vous avez migré avec succès votre base de données et rempli la table des produits avec trois éléments. Nous allons modifier le schéma de la base de données et ajouter une nouvelle colonne price
à la table products
, puis migrer et mettre à niveau notre base de données tout en préservant ces données à l'aide de Flask-Migrate.
Étape 04 - Modification du modèle de base de données
Vous avez maintenant une table de produits dans votre base de données avec quelques éléments qui y sont stockés. Dans cette étape, vous utiliserez Flask-Migrate pour ajouter une colonne prix
à la table produits
. Supposons que vous souhaitiez effectuer cette opération sans gestionnaire de migration de base de données. Dans ce cas, vous devez d'abord supprimer l'intégralité de la table products
et la recréer avec la nouvelle colonne, ce qui entraînera la perte de tous les éléments de produits existants. Cependant, avec Flask-Migrate, vous pouvez ajouter une nouvelle colonne tout en préservant les données existantes.
Ajout d'une nouvelle colonne de prix
Pour ajouter une colonne de prix à votre table de produits, ouvrez le fichier app.py
et modifiez le modèle de base de données Product
qui définit le schéma de la table.
Dans votre répertoire flask_app
, ouvrez app.py
pour modification :
nano app.py
Modifiez la classe Product()
en ajoutant une nouvelle colonne entière appelée price
:
class Product(db.Model):
__tablename__ = 'products'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
price = db.Column(db.Integer)
def __repr__(self):
return f'<Product {self.name}>'
Enregistrez et fermez le fichier.
La modification du schéma de votre base de données est désormais terminée. Vous allez générer un nouveau script de migration pour l'appliquer à la base de données tout en évitant la perte de données.
Générer une migration
Une fois que vous avez modifié votre modèle de base de données, exécutez la commande flask db migrate
pour que Flask-Migrate détecte votre modification et génère un nouveau script de migration. Assurez-vous d'ajouter un message décrivant ce que vous avez modifié :
flask db migrate -m "add price column"
Semblable à la migration initiale, vous recevrez un résultat comme celui-ci :
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added column 'products.price'
Generating
flask_app/migrations/versions/7ad34929a0f2_add_price_column.py
... done
Cela vous informe qu'une nouvelle colonne a été détectée et qu'un script de migration a été créé.
N'oubliez pas de revoir le script de migration généré. Dans ce cas, les fonctions principales upgrade()
et downgrade()
seront les suivantes :
def upgrade():
# ### commands auto-generated by Alembic - please adjust! ###
with op.batch_alter_table('products', schema=None) as batch_op:
batch_op.add_column(sa.Column('price', sa.Integer(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto-generated by Alembic - please adjust! ###
with op.batch_alter_table('products', schema=None) as batch_op:
batch_op.drop_column('price')
# ### end Alembic commands ###
Ici, la fonction upgrade()
modifie la table products
et ajoute une colonne price
. La fonction downgrade()
supprime la colonne price()
, restaurant ainsi votre base de données à la version précédente.
Mise à jour de la base de données
Après avoir modifié votre modèle de base de données, générez un script de migration basé sur cette modification. Vous pouvez maintenant appliquer les modifications à la base de données à l'aide de la commande upgrade
, qui exécute la fonction upgrade()
dans le dernier script de migration.
flask db upgrade
La sortie vous informera que la base de données a été déplacée de la version précédente vers une nouvelle version avec le "ajouter une colonne de prix"
comme message de migration.
Pour tester que la colonne price
a été ajoutée, exécutez le shell Flask :
flask shell
Ensuite, parcourez les éléments de produit dans la base de données et imprimez les valeurs des colonnes :
from app import db, Product
for p in Product.query.all():
print(p.name, p.id, p.price)
Le résultat devrait être le suivant :
Apple 1 None
Orange 2 None
Banana 3 None
Les valeurs Aucun
dans la sortie reflètent les valeurs de la nouvelle colonne de prix, et vous pouvez maintenant les modifier à votre guise pour refléter les prix de vos produits.
Si vous recevez une erreur lors de l'exécution de la boucle précédente, la colonne de prix n'a pas été ajoutée correctement et vous devez revoir attentivement les étapes précédentes, en vous assurant que vous avez correctement effectué toutes les actions nécessaires.
Quittez le shell Flask :
exit()
La base de données est désormais migrée vers une nouvelle version. Ensuite, vous allez rétrograder la base de données et supprimer la colonne prix
pour montrer comment restaurer un état précédent de la base de données.
Étape 05 - Rétrogradation de la base de données
À l'étape précédente, vous avez mis à niveau la version initiale de votre base de données et ajouté une colonne prix
à sa table produits
. Pour montrer comment restaurer un état antérieur lors de la gestion des migrations de bases de données, vous allez rétrograder votre base de données actuelle et supprimer la colonne prix
de la table produits
.
Pour rétrograder votre base de données et restaurer sa version précédente, exécutez la commande suivante dans votre répertoire flask_app
:
flask db downgrade
La sortie vous informera que la version de la base de données a changé et que la version précédente est restaurée.
Pour tester que la colonne price
est supprimée, ouvrez votre shell Flask :
flask shell
Exécutez ensuite une requête sur le modèle Product
pour récupérer tous les éléments de la table products :
from app import db, Product
Product.query.all()
Vous devriez recevoir une erreur indiquant que la table products
n'a pas de colonne price
:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: products.price
[SQL: SELECT products.id AS products_id, products.name AS products_name, products.price AS products_price
FROM products]
(Background on this error at: https://sqlalche.me/e/20/e3q8)
Cela confirme que la colonne price
a été supprimée avec succès et que la rétrogradation de la base de données a réussi. L'erreur se produit car vous avez toujours une colonne price
définie dans le Product( )
dans le fichier app.py
.
Pour corriger cette erreur, quittez le shell Flask :
exit()
Ensuite, ouvrez app.py
dans votre répertoire flask_app
:
nano app.py
Modifiez le modèle Product()
en supprimant la déclaration de la colonne price
pour que la version finale ressemble à ceci :
class Product(db.Model):
__tablename__ = 'products'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
def __repr__(self):
return f'<Product {self.name}>'
Enregistrez et fermez le fichier.
Pour tester que l'erreur a été corrigée, ouvrez le shell Flask :
flask shell
Ensuite, interrogez tous les produits :
from app import db, Product
Product.query.all()
Le résultat devrait être le suivant :
[<Product Apple>, <Product Orange>, <Product Banana>]
Cela indique que l'erreur a été corrigée.
Enfin, vous pouvez supprimer le fichier de migration qui contient add_price_column
dans son nom de fichier si vous n'en avez plus besoin :
rm migrations/versions/7ad34929a0f2_add_price_column.py
Avec cela, vous avez réussi à rétrograder votre base de données et à la restaurer à sa version précédente.
Conclusion
Vous avez créé une petite application Flask avec une base de données et l'avez intégrée à Flask-Migrate. Vous avez appris à modifier vos modèles de base de données, à les mettre à niveau vers une nouvelle version et à les rétrograder vers une version précédente.
En général, vous pouvez suivre les étapes suivantes pour gérer vos migrations de bases de données lorsque vous développez vos applications Flask :
Modifier les modèles de base de données.
Générez un script de migration avec la commande
flask db migrate
.Vérifiez le script de migration généré et corrigez-le si nécessaire.
Appliquez les modifications à la base de données avec la commande
flask db Upgrade
.Pour restaurer une version précédente de la base de données, utilisez la commande
flask db downgrade
.
Consultez la documentation Flask-Migrate pour plus d'informations.
Si vous souhaitez en savoir plus sur Flask, consultez les autres didacticiels de la série Comment créer des applications Web avec Flask.