Lo que está pidiendo es una migración de datos , a diferencia de la migración de esquema que es más frecuente en los documentos de Alembic.
Esta respuesta asume que está utilizando declarativo (a diferencia de class-Mapper-Table o core) para definir sus modelos. Debería ser relativamente sencillo adaptar esto a las otras formas.
Tenga en cuenta que Alembic proporciona algunas funciones de datos básicas: op.bulk_insert()
y op.execute()
. Si las operaciones son bastante mínimas, utilícelas. Si la migración requiere relaciones u otras interacciones complejas, prefiero usar todo el poder de los modelos y sesiones como se describe a continuación.
A continuación, se muestra un script de migración de ejemplo que configura algunos modelos declarativos que se utilizarán para manipular datos en una sesión. Los puntos clave son:
Defina los modelos básicos que necesita, con las columnas que necesitará. No necesita todas las columnas, solo la clave principal y las que utilizará.
Dentro de la función de actualización, use op.get_bind()
para obtener la conexión actual y hacer una sesión con ella.
- O use
bind.execute()
para usar el nivel inferior de SQLAlchemy para escribir consultas SQL directamente. Esto es útil para migraciones simples.
Utilice los modelos y la sesión como lo haría normalmente en su aplicación.
"""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 migración define modelos separados porque los modelos en su código representan el estado actual de la base de datos, mientras que las migraciones representan pasos en el camino . Su base de datos puede estar en cualquier estado a lo largo de esa ruta, por lo que es posible que los modelos aún no se sincronicen con la base de datos. A menos que tenga mucho cuidado, usar los modelos reales directamente causará problemas con columnas faltantes, datos no válidos, etc. Es más claro indicar explícitamente exactamente qué columnas y modelos utilizará en la migración.
op.execute
enupgrade()
, ¿hay una manera de proporcionar una plantilla predeterminada para ser utilizado poralembic revision
comandos (un cuerpo predeterminado para la generada.py
archivo)?