Есть ли способ python-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 позаботится о таких случаях в своей концепции/дизайне? Я хочу знать, могу ли я использовать для этого инструменты перегонного куба или мне нужно сделать для этого свой собственный дополнительный код.

Конечно, исходные данные немного сложнее преобразовать. Это просто пример здесь.


person buhtz    schedule 14.08.2015    source источник
comment
Как это julo.ch/blog/migrating-content-with-alembic ?   -  person Yaroslav Admin    schedule 14.08.2015
comment
Спасибо. Я бы тоже так поступил. Но это не перегонный способ в смысле соответствия концепции перегонного куба. Может быть, концепция/дизайн перегонного куба не заботится о таких случаях?   -  person buhtz    schedule 14.08.2015
comment
В Alembic есть метод для вставить данные. Но в нем нет ничего встроенного для обновления данных. Так что я думаю, что сделать это самостоятельно - это правильный путь.   -  person Yaroslav Admin    schedule 14.08.2015
comment
Я бы принял ваш комментарий в качестве ответа. ;)   -  person buhtz    schedule 16.08.2015


Ответы (1)


Вот справочник по эксплуатации. Существует метод под названием bulk_insert() для массовой вставки контент, но ничего для переноса существующего контента. Кажется, в перегонном кубе его нет. Но вы можете реализовать миграцию данных самостоятельно.

Один из возможных подходов описан в статье "Миграция контента с помощью alembic". Вам необходимо определить промежуточную таблицу внутри вашего файла миграции, которая содержит оба столбца (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)),
)

И используйте эту промежуточную таблицу для переноса данных из старого столбца в новый:

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')

Этот подход немного шумный (вам нужно определить таблицу вручную), но он работает.

person Yaroslav Admin    schedule 16.08.2015