Jen*_*ens 13 sqlalchemy-migrate python-3.x alembic
假设我的db模型包含一个对象User:
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(String(32), primary_key=True, default=...)
name = Column(Unicode(100))
Run Code Online (Sandbox Code Playgroud)
我的数据库包含一个包含n行的users表.在某些时候,我决定拆分成和,而在我想我的数据迁移以及.namefirstnamelastnamealembic upgrade head
自动生成的Alembic迁移如下:
def upgrade():
op.add_column('users', sa.Column('lastname', sa.Unicode(length=50), nullable=True))
op.add_column('users', sa.Column('firstname', sa.Unicode(length=50), nullable=True))
# Assuming that the two new columns have been committed and exist at
# this point, I would like to iterate over all rows of the name column,
# split the string, write it into the new firstname and lastname rows,
# and once that has completed, continue to delete the name column.
op.drop_column('users', 'name')
def downgrade():
op.add_column('users', sa.Column('name', sa.Unicode(length=100), nullable=True))
# Do the reverse of the above.
op.drop_column('users', 'firstname')
op.drop_column('users', 'lastname')
Run Code Online (Sandbox Code Playgroud)
对于这个问题,似乎存在多种或多或少的hacky解决方案.这一个和这个都建议在迁移期间使用execute()和bulk_insert()执行原始SQL语句.此(不完整)解决方案导入当前的数据库模型,但该模型更改时该方法很脆弱.
如何在Alembic迁移期间迁移和修改列数据的现有内容?建议的方式是什么,它在哪里记录?
Jen*_*ens 12
norbertpy的答案中提出的解决方案起初听起来不错,但我认为它有一个基本的缺陷:它会在步骤之间引入多个事务,DB将处于一个时髦,不一致的状态.我(看到我的评论)似乎很奇怪一个工具将在没有DB数据的情况下迁移DB的模式; 两者紧密地捆绑在一起将它们分开.
经过几次讨论和几次对话(参见本Gist中的代码片段),我决定采用以下解决方案:
def upgrade():
# Schema migration: add all the new columns.
op.add_column('users', sa.Column('lastname', sa.Unicode(length=50), nullable=True))
op.add_column('users', sa.Column('firstname', sa.Unicode(length=50), nullable=True))
# Data migration: takes a few steps...
# Declare ORM table views. Note that the view contains old and new columns!
t_users = sa.Table(
'users',
sa.MetaData(),
sa.Column('id', sa.String(32)),
sa.Column('name', sa.Unicode(length=100)), # Old column.
sa.Column('lastname', sa.Unicode(length=50)), # Two new columns.
sa.Column('firstname', sa.Unicode(length=50)),
)
# Use Alchemy's connection and transaction to noodle over the data.
connection = op.get_bind()
# Select all existing names that need migrating.
results = connection.execute(sa.select([
t_users.c.id,
t_users.c.name,
])).fetchall()
# Iterate over all selected data tuples.
for id_, name in results:
# Split the existing name into first and last.
firstname, lastname = name.rsplit(' ', 1)
# Update the new columns.
connection.execute(t_users.update().where(t_users.c.id == id_).values(
lastname=lastname,
firstname=firstname,
))
# Schema migration: drop the old column.
op.drop_column('users', 'name')
Run Code Online (Sandbox Code Playgroud)
关于此解决方案的两条评论
该downgrade()功能可以类似地实现.
附录.有关将模式迁移与数据迁移配对的示例,请参阅Alembic Cookbook中的" 条件迁移元素"部分.
alembic 是一个模式迁移工具,而不是数据迁移。虽然它也可以这样使用。这就是为什么你找不到很多相关文档的原因。也就是说,我会创建三个单独的修订版:
firstname并且lastname不删除name就像在应用程序中一样读取所有用户并拆分他们的名称,然后更新first和last。例如
for user in session.query(User).all():
user.firstname, user.lastname = user.name.split(' ')
session.commit()
Run Code Online (Sandbox Code Playgroud)消除name
| 归档时间: |
|
| 查看次数: |
5330 次 |
| 最近记录: |