是否有一种python-alembic方法可以在删除和添加列之间转换数据?

时间:2015-08-14 09:24:54

标签: python-3.x sqlite sqlalchemy alembic

我有一个sqlite3数据库,在SQLAlchemy中使用python3访问它。 我想添加一个新的,并使用数据库结合工具alembic删除旧列。简单的例子:

class Model(_Base):
  __tablename__ = 'Model'
  _oid = Column('oid', sa.Integer, primary_key=True)
  _number_int = sa.Column('number_int', sa.Integer)

应该在迁移后像这样:

class Model(_Base):
  __tablename__ = 'Model'
  _oid = Column('oid', sa.Integer, primary_key=True)
  _number_str = sa.Column('number_str', sa.String(length=30))

此处的相关要点是_number_int中的数据应该像这样转换为_number_str

number_conv = {1: 'one', 2: 'two', 3: 'three'}
_number_str = number_conv[_number_int]

是否有 alembic方式来照顾它?这意味着如果alembic本身在其概念/设计中处理类似的情况? 我想知道如果我可以使用alembic工具,或者我必须自己做额外的代码。

当然原始数据转换起来有点复杂。这只是一个例子。

1 个答案:

答案 0 :(得分:3)

这是alembic operation reference。有一种名为bulk_insert()的方法用于批量插入内容,但没有用于迁移现有内容的方法。似乎alembic并没有内置它。但您可以自己实现数据迁移。

文章"Migrating content with alembic"中描述了一种可能的方法。您需要在迁移文件中定义中间表,其中包含两列(number_intnumber_str):

import sqlalchemy as sa

model_helper = sa.Table(
    'Model',
    sa.MetaData(),
    sa.Column('oid', sa.Integer, primary_key=True),
    sa.Column('number_int', sa.Integer),
    sa.Column('number_str', sa.String(length=30)),
)

并使用此中间表将数据从旧列迁移到新列:

from alembic import op


def upgrade():
    # add the new column first
    op.add_column(
        'Model',
        sa.Column(
            'number_str',
            sa.String(length=30),
            nullable=True
        )
    )

    # build a quick link for the current connection of alembic
    connection = op.get_bind()

    # at this state right now, the old column is not deleted and the
    # new columns are present already. So now is the time to run the
    # content migration. We use the connection to grab all data from
    # the table, convert each number and update the row, which is 
    # identified by its id
    number_conv = {1: 'one', 2: 'two', 3: 'three'}
    for item in connection.execute(model_helper.select()):
        connection.execute(
            model_helper.update().where(
                model_helper.c.id == item.id
            ).values(
                number_str=number_conv[item.number_int]
            )
        )

    # now that all data is migrated we can just drop the old column
    # without having lost any data
    op.drop_column('Model', 'number_int')

这种方法有点吵(你需要手动定义表),但它可以工作。