使用Flask-SQLAlchemy在Alembic自动生成迁移时未检测到任何更改

问题描述 投票:18回答:4

我无法让Alembic使用db.Model(Flask-SQLAlchemy)而不是Base自动生成从更改到类的候选迁移。

我修改了env.py来创建我的Flask应用程序,导入所有相关模型,初始化数据库,然后运行迁移:

...
uri = 'mysql://user:password@host/dbname?charset=utf8'
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = uri
app.config['SQLALCHEMY_ECHO'] = True
db.init_app(app)
with app.test_request_context():
    target_metadata = db.Model.metadata
    config.set_main_option('sqlalchemy.url', uri)
    if context.is_offline_mode():
        run_migrations_offline()
    else:
        run_migrations_online()
...

这种方法适用于drop_all()create_all()(例如,在为单元测试重新创建测试数据库时),但在这种情况下它似乎没有变化。自动生成的版本脚本始终具有空的升级和降级方法,例如,

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    pass
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    pass
    ### end Alembic commands ###

我的更改包括重命名列,更改列定义等,而不仅仅是对索引和外键的更改。

有没有人在那里使用Alembic和Flask-SQLAlchemy?知道我哪里错了吗?

非常感谢!

sqlalchemy flask database-migration flask-sqlalchemy alembic
4个回答
36
投票

Alembic无法自动检测表或列重命名。默认情况下,它也不会查找列类型更改,但可以为此启用compare_type选项。

摘自Alembic文档:

默认情况下,Autogenerate将检测:

  • 表格增加,删除。
  • 列添加,删除。
  • 列上可空状态的更改。

Autogenerate可以选择检测:

  • 列类型的更改。如果你在compare_type=True上设置EnvironmentContext.configure(),就会发生这种情况。该功能在大多数情况下运行良好,但默认情况下处于关闭状态,因此可以首先在目标模式上对其进行测试。它也可以通过传递一个可调用来定制;有关详细信息,请参阅功能文档。
  • 更改服务器默认值。如果你在compare_server_default=True上设置EnvironmentContext.configure(),就会发生这种情况。此功能适用于简单的情况,但不能始终产生准确的结果。 Postgresql后端实际上将针对数据库调用“检测到的”和“元数据”值以确定等效性。默认情况下,该功能处于关闭状态,因此可以首先在目标模式上对其进行测试。像类型比较一样,它也可以通过传递一个可调用来定制;有关详细信息,请参阅功能文档。

自动生成无法检测到:

  • 表名的更改。这些将作为两个不同表的添加/删除出现,应该手动编辑为名称更改。
  • 列名称的更改。与表名更改一样,这些更改会被检测为列添加/删除对,这与名称更改完全不同。
  • 特殊的SQLAlchemy类型,例如Enum,当在后端生成时不直接支持ENUM - 这是因为在非支持数据库中这种类型的表示,即CHAR+CHECK约束,可以是任何类型的CHAR+CHECK。对于SQLAlchemy来确定这实际上是一个ENUM只是一个猜测,这通常是一个坏主意。要在此处实现您自己的“猜测”功能,请使用sqlalchemy.events.DDLEvents.column_reflect()事件来更改为某些列传递的SQLAlchemy类型,并可能使用sqlalchemy.events.DDLEvents.after_parent_attach()来拦截不需要的CHECK约束。

Autogenerate目前无法进行,但最终会检测到:

  • 独立的约束增加,删除,如CHECKUNIQUEFOREIGN KEY - 这些尚未实施。现在你将在新表中获得约束,PK和FK约束用于“降级”到先前存在的表,并且使用SQLAlchemy“schema”类型生成的CHECK约束类型为BooleanEnum
  • 指数增加,删除 - 尚未实施。
  • 序列添加,删除 - 尚未实现。

更新:Alembic 0.7.x版本支持最后一个列表中的一些项目。


4
投票

我的错误是尝试创建我的初始迁移,db已经处于最终状态,认为它会注意到它没有现有版本并且基于模型。我得到了空的版本,直到我删除了数据库中的所有表,然后它工作正常。


0
投票

尝试flask-alembic https://github.com/tobiasandtobias/flask-alembic

我昨天试过了。除drop操作外,它对我来说很好。它们不适用于sqlite(https://bitbucket.org/zzzeek/alembic/issue/21/column-renames-not-supported-on-sqlite)。

我用它的方式。首先,我使用python manage.py migrate revision --autogenerate在sqlite db中创建空表。它会产生这样的迁移

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.create_table('users_user',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=50), nullable=True),
    sa.Column('email', sa.String(length=120), nullable=True),
    sa.Column('password', sa.String(length=20), nullable=True),
    sa.Column('role', sa.SmallInteger(), nullable=True),
    sa.Column('status', sa.SmallInteger(), nullable=True),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('email'),
    sa.UniqueConstraint('name')
)
### end Alembic commands ###

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

然后 - python manage.py migrate upgrade head

然后我将一个新列test = db.Column(db.String(20))添加到User模型并运行此命令python manage.py migrate revision --autogenerate -m 'test field at users'

这产生了这样的迁移:

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.add_column('users_user', sa.Column('test', sa.String(length=20), nullable=True))
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('users_user', 'test')
    ### end Alembic commands ###

0
投票

我也遇到过这个问题并用这种方式来解决这个问题:

打开migrations/env.py文件,并在def run_migrations_online()函数上查看context.configure,在Alembic 1.0.8上它应该如下所示:

with connectable.connect() as connection:
    context.configure(
        connection=connection,
        target_metadata=target_metadata,
        process_revision_directives=process_revision_directives,
        **current_app.extensions['migrate'].configure_args,
    )

只需删除或评论theprocess_revision_directives=process_revision_directives,然后在其上添加compare_type=True

像这样:

with connectable.connect() as connection:
    context.configure(
        connection=connection,
        target_metadata=target_metadata,
        # process_revision_directives=process_revision_directives,
        **current_app.extensions['migrate'].configure_args,
        compare_type=True
    )

-1
投票

如果你发现你需要的功能不支持alembic。在这种情况下

  1. 删除表(或列)然后迁移
  2. 添加表(或列)然后再次迁移

这也适用于枚举类型更改

© www.soinside.com 2019 - 2024. All rights reserved.