Подтвердить что ты не робот

Создание данных семян в миграциях с флягой или миграции

Как я могу вставить некоторые исходные данные в первую миграцию? Если миграция не является лучшим местом для этого, то в чем лучшая практика?

"""empty message

Revision ID: 384cfaaaa0be
Revises: None
Create Date: 2013-10-11 16:36:34.696069

"""

# revision identifiers, used by Alembic.
revision = '384cfaaaa0be'
down_revision = None

from alembic import op
import sqlalchemy as sa


def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.create_table('list_type',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=80), nullable=False),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('name')
    )
    op.create_table('job',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('list_type_id', sa.Integer(), nullable=False),
    sa.Column('record_count', sa.Integer(), nullable=False),
    sa.Column('status', sa.Integer(), nullable=False),
    sa.Column('sf_job_id', sa.Integer(), nullable=False),
    sa.Column('created_at', sa.DateTime(), nullable=False),
    sa.Column('compressed_csv', sa.LargeBinary(), nullable=True),
    sa.ForeignKeyConstraint(['list_type_id'], ['list_type.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    ### end Alembic commands ###

    # ==> INSERT SEED DATA HERE <==


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('job')
    op.drop_table('list_type')
    ### end Alembic commands ###
4b9b3361

Ответ 1

Alembic, как одна из его операций, bulk_insert(). В документации приведен следующий пример (с некоторыми исправлениями, которые я включил):

from datetime import date
from sqlalchemy.sql import table, column
from sqlalchemy import String, Integer, Date
from alembic import op

# Create an ad-hoc table to use for the insert statement.
accounts_table = table('account',
    column('id', Integer),
    column('name', String),
    column('create_date', Date)
)

op.bulk_insert(accounts_table,
    [
        {'id':1, 'name':'John Smith',
                'create_date':date(2010, 10, 5)},
        {'id':2, 'name':'Ed Williams',
                'create_date':date(2007, 5, 27)},
        {'id':3, 'name':'Wendy Jones',
                'create_date':date(2008, 8, 15)},
    ]
)

Заметим также, что alembic имеет execute() операцию, которая похожа на обычную функцию execute() в SQLAlchemy: вы можете запустить любой SQL, который вы хотите, как показывает пример документации:

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

Обратите внимание, что таблица, которая используется для создания метаданных, используемых в операторе update, определяется непосредственно в схеме. Это может показаться, что он разбивает DRY (это не та таблица, которая уже определена в вашем приложении), но на самом деле совершенно необходима. Если вы попытаетесь использовать определение таблицы или модели, которое является частью вашего приложения, вы можете разбить эту миграцию, когда вы вносите изменения в свою таблицу/модель в своем приложении. Ваши сценарии миграции должны быть установлены в виде камня: изменение в будущей версии ваших моделей не должно изменять сценарии миграции. Использование моделей приложений будет означать, что определения будут меняться в зависимости от того, какую версию моделей вы проверили (скорее всего, последние). Поэтому вам необходимо, чтобы определение таблицы было самодостаточным в миграции script.

Еще одна вещь, о которой стоит поговорить, заключается в том, следует ли помещать ваши данные семени в script, который запускается как его собственная команда (например, с помощью команды Flask- Script, как показано в другом ответе). Это можно использовать, но вы должны быть осторожны. Если данные, которые вы загружаете, являются тестовыми данными, то это одно. Но я понял, что "данные семени" означают данные, необходимые для правильной работы приложения. Например, если вам нужно настроить записи для "admin" и "user" в таблице "role". Эти данные ДОЛЖНЫ быть вставлены как часть миграций. Помните, что script будет работать только с последней версией вашей базы данных, тогда как миграция будет работать с конкретной версией, которую вы переносите в или из нее. Если вы хотите, чтобы script загружал информацию о ролях, вам может понадобиться script для каждой версии базы данных с другой схемой для таблицы ролей.

Кроме того, полагаясь на script, вам будет труднее запустить script между миграциями (например, для миграции 3 > 4 требуется, чтобы начальные данные в начальной миграции были в базе данных). Теперь вам нужно изменить способ запуска Alembic по умолчанию для запуска этих сценариев. И это все еще не игнорирует проблемы, связанные с тем, что эти сценарии со временем будут меняться, и кто знает, какую версию вашего приложения вы проверили из источника управления.

Ответ 2

Миграции должны быть ограничены только изменениями схемы, и не только это важно, чтобы при миграции вверх или вниз сохранялись как можно больше данных, которые существовали в базе данных ранее. Вставка данных семян в часть миграции может испортить ранее существовавшие данные.

Как и большинство вещей с Flask, вы можете реализовать это разными способами. Добавление новой команды в Flask- Script - хороший способ сделать это, на мой взгляд. Например:

@manager.command
def seed():
    "Add seed data to the database."
    db.session.add(...)
    db.session.commit()

Итак, вы запустите:

python manager.py seed

Ответ 3

MarkHildreth предоставил отличное объяснение того, как этот метод может справиться с этим. Тем не менее, ОП был конкретно о том, как изменить миграцию миграции колб script. Я собираюсь опубликовать ответ на это ниже, чтобы спасти людей от времени, чтобы заглянуть в alembic вообще.

Внимание Ответ Miguel является точным в отношении обычной информации о базе данных. То есть следует следовать его совету и абсолютно не использовать этот подход для заполнения базы данных "нормальными" строками. Этот подход специально предназначен для строк базы данных, которые необходимы для работы приложения, своего рода данных, которые я считаю "семенными" данными.

OP script изменен для обработки данных:

"""empty message

Revision ID: 384cfaaaa0be
Revises: None
Create Date: 2013-10-11 16:36:34.696069

"""

# revision identifiers, used by Alembic.
revision = '384cfaaaa0be'
down_revision = None

from alembic import op
import sqlalchemy as sa


def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    list_type_table = op.create_table('list_type',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=80), nullable=False),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('name')
    )
    op.create_table('job',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('list_type_id', sa.Integer(), nullable=False),
    sa.Column('record_count', sa.Integer(), nullable=False),
    sa.Column('status', sa.Integer(), nullable=False),
    sa.Column('sf_job_id', sa.Integer(), nullable=False),
    sa.Column('created_at', sa.DateTime(), nullable=False),
    sa.Column('compressed_csv', sa.LargeBinary(), nullable=True),
    sa.ForeignKeyConstraint(['list_type_id'], ['list_type.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    ### end Alembic commands ###


    op.bulk_insert(
        list_type_table,
        [
            {'name':'best list'},
            {'name': 'bester list'}
        ]
    )


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('job')
    op.drop_table('list_type')
    ### end Alembic commands ###

Контекст для новых для flask_migrate

Миграция флагов генерирует сценарии миграции в migrations/versions. Эти сценарии запускаются в порядке в базе данных, чтобы довести их до последней версии. OP включает пример одного из этих автоматически сгенерированных сценариев миграции. Чтобы добавить данные семян, необходимо вручную изменить соответствующий автоматически сгенерированный файл миграции. Код, который я опубликовал выше, является примером этого.

Что изменилось?

Очень мало. Вы заметите, что в новом файле я сохраняю таблицу, возвращенную из create_table для list_type в переменной с именем list_type_table. Затем мы используем эту таблицу, используя op.bulk_insert, чтобы создать несколько строк.