То, что вы просите, - это миграция данных , в отличие от миграции схемы, которая наиболее распространена в документации Alembic.
Этот ответ предполагает, что вы используете декларативный (в отличие от class-Mapper-Table или core) для определения ваших моделей. Это должно быть относительно просто адаптировать к другим формам.
Обратите внимание, что Alembic предоставляет некоторые основные функции обработки данных: op.bulk_insert()
и op.execute()
. Если операции довольно минимальны, используйте их. Если для миграции требуются отношения или другие сложные взаимодействия, я предпочитаю использовать все возможности моделей и сеансов, как описано ниже.
Ниже приведен пример сценария миграции, который устанавливает некоторые декларативные модели, которые будут использоваться для управления данными в сеансе. Ключевые моменты:
Определите основные модели, которые вам нужны, с нужными столбцами. Вам не нужны все столбцы, только первичный ключ и те, которые вы будете использовать.
В функции обновления используйте op.get_bind()
для получения текущего соединения и создания с ним сеанса.
- Или используйте,
bind.execute()
чтобы использовать более низкий уровень SQLAlchemy для непосредственного написания SQL-запросов. Это полезно для простых миграций.
Используйте модели и сеанс, как обычно в своем приложении.
"""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)
Team.__table__.create(bind)
op.add_column('players', sa.Column('team_id', sa.ForeignKey('teams.id'), nullable=False)
teams = {name: Team(name=name) for name in session.query(Player.team).distinct()}
session.add_all(teams.values())
for player in session.query(Player):
player.team = teams[player.team_name]
session.commit()
op.drop_column('players', 'team')
def downgrade():
bind = op.get_bind()
session = orm.Session(bind=bind)
op.add_column('players', sa.Column('team', sa.String, nullable=False)
for player in session.query(Player):
player.team_name = player.team.name
session.commit()
op.drop_column('players', 'team_id')
op.drop_table('teams')
The migration defines separate models because the models in your code represent the current state of the database, while the migrations represent steps along the way. Your database might be in any state along that path, so the models might not sync up with the database yet. Unless you're very careful, using the real models directly will cause problems with missing columns, invalid data, etc. It's clearer to explicitly state exactly what columns and models you will use in the migration.
op.execute
inupgrade()
, is there a way to provide a default template to be used byalembic revision
command (a default body for the generated.py
file)?