Ответ 1
То, о чем вы просите, - это миграция данных, а не миграция схемы, которая наиболее распространена в документах 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)
# 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')
Миграция определяет отдельные модели, потому что модели в вашем коде представляют текущее состояние базы данных, в то время как миграции представляют собой шаги на этом пути. Ваша база данных может находиться в любом состоянии по этому пути, поэтому модели могут еще не синхронизироваться с базой данных. Если вы не будете очень осторожны, непосредственное использование реальных моделей вызовет проблемы с отсутствующими столбцами, неверными данными и т.д. Будет проще четко указать, какие столбцы и модели вы будете использовать при переносе.