是否有 python-alembic 方法在删除和添加列之间转换数据?
Is there a python-alembic way to convert data between dropping and adding a column?
我有一个 sqlite3
数据库在 python3
中使用 SQLAlchemy
访问它。
我想使用数据库迁移工具 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 本身是否会处理其 concept/design 中的情况?
我想知道我是否可以为此使用 alembic 工具,或者我是否必须为此编写自己的额外代码。
当然,原始数据转换起来稍微复杂一些。这只是一个例子。
这里是 alembic operation reference. There is a method called bulk_insert()
用于批量插入内容,但没有用于迁移现有内容。似乎蒸馏器没有内置。但是你可以自己实现数据迁移。
文章 "Migrating content with alembic" 中描述了一种可能的方法。您需要在迁移文件中定义中间 table,其中包含两列(number_int
和 number_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)),
)
并使用这个中间体 table 将数据从旧列迁移到新列:
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')
这种方法有点嘈杂(您需要手动定义 table),但它确实有效。
我有一个 sqlite3
数据库在 python3
中使用 SQLAlchemy
访问它。
我想使用数据库迁移工具 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 本身是否会处理其 concept/design 中的情况? 我想知道我是否可以为此使用 alembic 工具,或者我是否必须为此编写自己的额外代码。
当然,原始数据转换起来稍微复杂一些。这只是一个例子。
这里是 alembic operation reference. There is a method called bulk_insert()
用于批量插入内容,但没有用于迁移现有内容。似乎蒸馏器没有内置。但是你可以自己实现数据迁移。
文章 "Migrating content with alembic" 中描述了一种可能的方法。您需要在迁移文件中定义中间 table,其中包含两列(number_int
和 number_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)),
)
并使用这个中间体 table 将数据从旧列迁移到新列:
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')
这种方法有点嘈杂(您需要手动定义 table),但它确实有效。