Ви вимагаєте міграції даних , на відміну від міграції схеми, яка найбільш поширена в документах 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')
Міграція визначає окремі моделі, оскільки моделі у вашому коді представляють поточний стан бази даних, тоді як міграції представляють кроки на шляху . Ваша база даних може знаходитись у будь-якому стані на цьому шляху, тому моделі можуть ще не синхронізуватися з базою даних. Якщо ви не будете дуже обережними, безпосереднє використання реальних моделей призведе до проблем із відсутніми стовпцями, недійсними даними тощо. Ясніше чітко вказати, які саме стовпці та моделі ви будете використовувати при міграції.
op.execute
вupgrade()
, чи є спосіб надати шаблон за замовчуванням, який буде використовуватисяalembic revision
командою (тіло за замовчуванням для згенерованого.py
файлу)?