26

我无法让 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 吗?知道我哪里出错了吗?

非常感谢!

4

5 回答 5

55

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

摘自 Alembic 文档:

默认情况下,自动生成将检测:

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

自动生成可以选择检测:

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

自动生成无法检测到:

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

Autogenerate 目前不能,但最终会检测到:

  • 独立的约束添加、删除,如CHECK, UNIQUE, FOREIGN KEY- 这些尚未实现。现在,您将获得新表中的约束、“降级”到先前存在的表的 PK 和 FK 约束,以及CHECK使用 SQLAlchemy“模式”类型生成Boolean的约束Enum
  • 索引添加、删除 - 尚未实施。
  • 序列添加、删除 - 尚未实施。

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

于 2013-06-22T00:13:25.997 回答
4

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

于 2016-08-08T13:34:42.883 回答
4

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

打开migrations/env.py文件,在def run_migrations_online()功能上查看context.configureAlembic 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,
    )

只需删除或评论process_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
    )
于 2019-08-06T14:20:55.670 回答
2

这可能是一个愚蠢的建议,但我遇到了类似的问题。我所有的 env 文件都指向了正确的位置等,但我仍然无法生成这个新表。我正在使用命令序列“alembic revision -m 'comment'”,然后是“alembic upgrade head”,我得到了“空”修订文件。

最后,在删除所有迁移文件、销毁 docker 映像、恢复迁移文件、重新尝试上述 cli 命令序列以及手动手动编码迁移之前,我尝试了“alembic revision --autogenerate”,它找到并创建了表的迁移。

这可能是由于我之前采取的所有其他步骤。如果它不是很有帮助,我很抱歉。

于 2020-09-29T02:40:44.617 回答
-1

尝试烧瓶-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))向用户模型添加了一个新列并运行了这个命令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 ###
于 2012-09-19T09:03:07.877 回答