Are*_*k S 75 python sqlalchemy alembic
我需要在Alembic升级期间更改数据.
我目前在第一次修订中有一个'玩家'表:
def upgrade():
op.create_table('player',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.Unicode(length=200), nullable=False),
sa.Column('position', sa.Unicode(length=200), nullable=True),
sa.Column('team', sa.Unicode(length=100), nullable=True)
sa.PrimaryKeyConstraint('id')
)
Run Code Online (Sandbox Code Playgroud)
我想介绍一个'团队'表.我创建了第二个版本:
def upgrade():
op.create_table('teams',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=80), nullable=False)
)
op.add_column('players', sa.Column('team_id', sa.Integer(), nullable=False))
Run Code Online (Sandbox Code Playgroud)
我想第二次迁移也添加以下数据:
填充团队表:
INSERT INTO teams (name) SELECT DISTINCT team FROM players;
Run Code Online (Sandbox Code Playgroud)根据players.team名称更新players.team_id:
UPDATE players AS p JOIN teams AS t SET p.team_id = t.id WHERE p.team = t.name;
Run Code Online (Sandbox Code Playgroud)如何在升级脚本中执行插入和更新?
dav*_*ism 126
您要求的是数据迁移,而不是Alembic文档中最常见的模式迁移.
这个答案假设您使用声明式(而不是类Mapper-Table或核心)来定义模型.将其与其他形式相适应应该相对简单.
请注意,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')
Run Code Online (Sandbox Code Playgroud)
迁移定义了单独的模型,因为代码中的模型代表数据库的当前状态,而迁移代表了整个过程中的步骤.您的数据库可能处于该路径的任何状态,因此模型可能尚未与数据库同步.除非您非常小心,否则直接使用真实模型会导致列缺失,数据无效等问题.明确说明您将在迁移中使用哪些列和模型更清楚.
我建议使用使用临时表的 SQLAlchemy 核心语句,如官方文档中所述,因为它允许使用不可知的 SQL 和 pythonic 编写,并且也是自包含的。SQLAlchemy Core 是迁移脚本两全其美的。
下面是这个概念的一个例子:
from sqlalchemy.sql import table, column
from sqlalchemy import String
from alembic import op
account = table('account',
column('name', String)
)
op.execute(
account.update().\\
where(account.c.name==op.inline_literal('account 1')).\\
values({'name':op.inline_literal('account 2')})
)
# If insert is required
from sqlalchemy.sql import insert
from sqlalchemy import orm
session = orm.Session(bind=bind)
bind = op.get_bind()
data = {
"name": "John",
}
ret = session.execute(insert(account).values(data))
# for use in other insert calls
account_id = ret.lastrowid
Run Code Online (Sandbox Code Playgroud)
You can also use direct SQL see (Alembic Operation Reference) as in the following example:
from alembic import op
# revision identifiers, used by Alembic.
revision = '1ce7873ac4ced2'
down_revision = '1cea0ac4ced2'
branch_labels = None
depends_on = None
def upgrade():
# ### commands made by andrew ###
op.execute('UPDATE STOCK SET IN_STOCK = -1 WHERE IN_STOCK IS NULL')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
Run Code Online (Sandbox Code Playgroud)