如何在第一次迁移中插入一些种子数据?如果迁移不是最佳选择,那么最佳实践是什么?
"""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 ###
Alembic作为其操作之一bulk_insert()。该文档提供了以下示例(包含一些我已修复的问题):
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,如文档示例所示:
execute()
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(不是您的应用程序中已经定义的表),但实际上是非常必要的。如果要尝试使用属于应用程序的表或模型定义,则在对应用程序中的表/模型进行更改时,将中断此迁移。您的迁移脚本应该一成不变:对模型的未来版本的更改不应更改迁移脚本。使用应用程序模型将意味着定义将根据您检出的模型版本(最可能是最新版本)而改变。因此,您需要表定义在迁移脚本中是独立的。
update
要讨论的另一件事是,是否应将种子数据放入作为自己的命令运行的脚本中(例如,使用Flask- Script命令,如其他答案所示)。可以使用此方法,但是您应该注意这一点。如果您要加载的数据是测试数据,那是一回事。但是我已经理解“种子数据”是指应用程序正常工作所需的数据。例如,如果您需要在“角色”表中设置“管理员”和“用户”的记录。该数据应作为迁移的一部分插入。请记住,脚本仅适用于数据库的最新版本,而迁移将适用于要迁移到的或从其中迁移的特定版本。如果您希望脚本加载角色信息,
另外,依靠脚本,您将在迁移之间运行脚本变得更加困难(例如,迁移3-> 4要求初始迁移中的种子数据位于数据库中)。现在,您需要修改Alembic的默认运行方式以运行这些脚本。而且,这些脚本仍将随着时间的推移而变化,并且谁知道您已经从源代码管理中签出了哪个版本的应用程序,仍然不能忽略这些问题。