58

最初の移行でシード データを挿入するにはどうすればよいですか? 移行がこれに最適な場所ではない場合、ベスト プラクティスは何ですか?

"""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 ###
4

5 に答える 5

84

Alembic は、その操作の 1 つとして、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)},
    ]
)

execute()alembic には、SQLAlchemyの通常の関数と同様の操作があることにも注意してくださいexecute()。ドキュメントの例に示すように、任意の 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を破るように見えるかもしれません(アプリケーションでテーブルがすでに定義されていません)が、実際には非常に必要です。アプリケーションの一部であるテーブルまたはモデル定義を使用しようとすると、アプリケーションでテーブル/モデルに変更を加えると、この移行が中断されます。移行スクリプトは固定されている必要があります。モデルの将来のバージョンに変更を加えても、移行スクリプトは変更されません。アプリケーション モデルを使用すると、チェックアウトしたモデルのバージョン (ほとんどの場合最新) に応じて定義が変更されます。したがって、テーブル定義は移行スクリプトで自己完結型にする必要があります。

話し合うべきもう1つのことは、シードデータを独自のコマンドとして実行されるスクリプトに入れる必要があるかどうかです(他の回答に示されているように、Flask-Scriptコマンドを使用するなど)。使えますが、注意が必要です。読み込んでいるデータがテスト データである場合、それは 1 つのことです。しかし、「シード データ」とは、アプリケーションが正しく動作するために必要なデータを意味すると理解しています。たとえば、「ロール」テーブルに「管理者」と「ユーザー」のレコードを設定する必要がある場合。このデータは、移行の一部として挿入する必要があります。スクリプトはデータベースの最新バージョンでのみ機能するのに対し、移行は移行先または移行元の特定のバージョンで機能することに注意してください。ロール情報をロードするスクリプトが必要な場合は、

また、スクリプトに依存することで、移行の間にスクリプトを実行することがより困難になります (たとえば、移行 3->4 では、最初の移行のシード データがデータベースにある必要があります)。これらのスクリプトを実行するには、Alembic のデフォルトの実行方法を変更する必要があります。そして、これらのスクリプトは時間の経過とともに変更する必要があり、ソース管理からチェックアウトしたアプリケーションのバージョンを誰が知っているかという問題を無視していません。

于 2013-10-12T19:30:50.987 に答える
31

移行はスキーマの変更のみに限定する必要があり、それだけでなく、移行のアップまたはダウンが適用されるときに、以前からデータベースに存在していたデータが可能な限り保持されることが重要です。移行の一部としてシード データを挿入すると、既存のデータが台無しになる場合があります。

Flask のほとんどのことと同様に、これはさまざまな方法で実装できます。私の意見では、Flask-Script に新しいコマンドを追加することは、これを行うための良い方法です。例えば:

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

したがって、次のように実行します。

python manager.py seed
于 2013-10-12T18:33:35.680 に答える