Mar*_*man 43 python flask flask-sqlalchemy alembic flask-migrate
如何在第一次迁移中插入一些种子数据?如果迁移不是最好的地方,那么最佳做法是什么?
"""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 ###
Mar*_*eth 66
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(不是您的应用程序中已定义的表),但实际上非常必要.如果您尝试使用属于应用程序的表或模型定义,则在应用程序中对表/模型进行更改时,将会中断此迁移.您的迁移脚本应该一成不变:对模型的未来版本的更改不应更改迁移脚本.使用应用程序模型意味着定义将根据您检出的模型版本(最可能是最新版本)而改变.因此,您需要在迁移脚本中自定义表定义.
另一件要说的是你是否应该将种子数据放入一个作为自己的命令运行的脚本(例如使用Flask-Script命令,如另一个答案中所示).这可以使用,但你应该小心.如果您加载的数据是测试数据,那么这是一回事.但我理解"种子数据"是指应用程序正常工作所需的数据.例如,如果您需要在"角色"表中为"admin"和"user"设置记录.该数据应该作为迁移的一部分插入.请记住,脚本只能与最新版本的数据库一起使用,而迁移将与您要迁移到的特定版本一起使用.如果您希望脚本加载角色信息,
此外,通过依赖脚本,您将使在迁移之间运行脚本变得更加困难(例如,迁移3-> 4要求初始迁移中的种子数据位于数据库中).您现在需要修改Alembic的默认运行方式来运行这些脚本.而且这仍然没有忽视这些脚本必须随时间变化的问题,以及谁知道您从源代码管理中检出的应用程序的版本.
Mig*_*uel 25
迁移应仅限于模式更改,不仅如此,重要的是,当应用向上或向下迁移时,数据库中存在的数据将尽可能保留.作为迁移的一部分插入种子数据可能会破坏预先存在的数据.
与Flask的大多数事情一样,您可以通过多种方式实现这一点.在我看来,向Flask-Script添加一个新命令是一种很好的方法.例如:
@manager.command
def seed():
    "Add seed data to the database."
    db.session.add(...)
    db.session.commit()
那么你运行:
python manager.py seed
您还可以使用 Python 的 faker 库,它可能会更快一些,因为您不需要自己提供任何数据。配置它的一种方法是将一个方法放入您想要为其生成数据的类中,如下所示。
from extensions import bcrypt, db
class User(db.Model):
    # this config is used by sqlalchemy to store model data in the database
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(150))
    email = db.Column(db.String(100), unique=True)
    password = db.Column(db.String(100))
    def __init__(self, name, email, password, fav_movie):
        self.name = name
        self.email = email
        self.password = password
    @classmethod
    def seed(cls, fake):
        user = User(
            name = fake.name(),
            email = fake.email(),
            password = cls.encrypt_password(fake.password()),
        )
        user.save()
    @staticmethod
    def encrypt_password(password):
        return bcrypt.generate_password_hash(password).decode('utf-8')
    def save(self):
        db.session.add(self)
        db.session.commit()
然后实现一个调用种子方法的方法,该方法可能如下所示:
from faker import Faker
from users.models import User
fake = Faker()
    for _ in range(100):
        User.seed(fake)
MarkHildreth提供了一个很好的解释,说明了Alembic如何处理此问题。但是,OP专门涉及如何修改flask-migration迁移脚本。我将在下面发布该问题的答案,以使人们不必再花些时间去研究除菌剂。
警告 Miguel的答案相对于常规数据库信息而言是准确的。就是说,应该遵循他的建议,绝对不要使用这种方法来用“正常”行填充数据库。这种方法专门用于应用程序运行所需的数据库行,我认为这是一种“种子”数据。
OP的脚本已修改为播种数据:
"""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 migration在生成迁移脚本migrations/versions。这些脚本在数据库上按顺序运行,以使其达到最新版本。该OP包括这些自动生成的迁移脚本之一的示例。为了添加种子数据,必须手动修改适当的自动生成的迁移文件。我上面发布的代码就是一个例子。
发生了什么变化?
很少 您会注意到,在新文件中,我将从create_tablefor 返回的表存储在list_type名为的变量中list_type_table。然后,我们使用op.bulk_insert创建一些示例行来对该表进行操作。
| 归档时间: | 
 | 
| 查看次数: | 21778 次 | 
| 最近记录: |