Ce que vous demandez, c'est une migration de données , par opposition à la migration de schéma qui est la plus répandue dans la documentation Alembic.
Cette réponse suppose que vous utilisez déclarative (par opposition à class-Mapper-Table ou core) pour définir vos modèles. Il devrait être relativement simple de l'adapter aux autres formes.
Notez qu'Alembic fournit quelques fonctions de données de base: op.bulk_insert()
et op.execute()
. Si les opérations sont assez minimes, utilisez-les. Si la migration nécessite des relations ou d'autres interactions complexes, je préfère utiliser toute la puissance des modèles et des sessions comme décrit ci-dessous.
Voici un exemple de script de migration qui configure certains modèles déclaratifs qui seront utilisés pour manipuler les données dans une session. Les points clés sont:
Définissez les modèles de base dont vous avez besoin, avec les colonnes dont vous aurez besoin. Vous n'avez pas besoin de toutes les colonnes, seulement de la clé primaire et de celles que vous utiliserez.
Dans la fonction de mise à niveau, utilisez op.get_bind()
pour obtenir la connexion actuelle et créer une session avec elle.
- Ou utilisez
bind.execute()
pour utiliser le niveau inférieur de SQLAlchemy pour écrire directement des requêtes SQL. Ceci est utile pour les migrations simples.
Utilisez les modèles et la session comme vous le feriez normalement dans votre application.
"""create teams table
Revision ID: 169ad57156f0
Revises: 29b4c2bfce6d
Create Date: 2014-06-25 09:00:06.784170
"""
revision = '169ad57156f0'
down_revision = '29b4c2bfce6d'
from alembic import op
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Player(Base):
__tablename__ = 'players'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String, nullable=False)
team_name = sa.Column('team', sa.String, nullable=False)
team_id = sa.Column(sa.Integer, sa.ForeignKey('teams.id'), nullable=False)
team = orm.relationship('Team', backref='players')
class Team(Base):
__tablename__ = 'teams'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String, nullable=False, unique=True)
def upgrade():
bind = op.get_bind()
session = orm.Session(bind=bind)
# create the teams table and the players.team_id column
Team.__table__.create(bind)
op.add_column('players', sa.Column('team_id', sa.ForeignKey('teams.id'), nullable=False)
# create teams for each team name
teams = {name: Team(name=name) for name in session.query(Player.team).distinct()}
session.add_all(teams.values())
# set player team based on team name
for player in session.query(Player):
player.team = teams[player.team_name]
session.commit()
# don't need team name now that team relationship is set
op.drop_column('players', 'team')
def downgrade():
bind = op.get_bind()
session = orm.Session(bind=bind)
# re-add the players.team column
op.add_column('players', sa.Column('team', sa.String, nullable=False)
# set players.team based on team relationship
for player in session.query(Player):
player.team_name = player.team.name
session.commit()
op.drop_column('players', 'team_id')
op.drop_table('teams')
La migration définit des modèles distincts car les modèles de votre code représentent l' état actuel de la base de données, tandis que les migrations représentent les étapes en cours de route . Votre base de données peut être dans n'importe quel état le long de ce chemin, de sorte que les modèles peuvent ne pas encore se synchroniser avec la base de données. Sauf si vous êtes très prudent, l'utilisation directe des modèles réels entraînera des problèmes de colonnes manquantes, de données invalides, etc. Il est plus clair d'indiquer explicitement les colonnes et les modèles que vous utiliserez dans la migration.
op.execute
enupgrade()
, est - il un moyen de fournir un modèle par défaut à utiliser paralembic revision
commande (un corps par défaut du produit.py
fichier)?