如何将 flask-migrate 与其他工具一起使用 declarative_bases

How to use flask-migrate with other declarative_bases

我正在尝试在 Flask 中实施 python-social-auth。在尝试同时解释大约 4 个教程和一本完整的 Flask-book 时,我已经解决了很多问题,并且感觉我已经陷入了 Flask-migrate 的僵局。

我目前正在使用以下代码来创建 python-social-auth 在 flask-sqlalchemy 环境中运行所需的 tables。

from social.apps.flask_app.default import models
models.PSABase.metadata.create_all(db.engine)

现在,他们显然在使用某种形式的他们自己的 Base,与我的实际 db-object 无关。这反过来会导致 Flask-Migrate 完全错过这些 tables 并在迁移中删除它们。现在,显然我可以从每次删除中删除这些 db-drops,但我可以想象它是其中一个在某一时刻会被遗忘的东西,突然之间我不再有 OAuth-ties 了。

我已经按照 the python-social-auth Flask example

的建议使用 manage.py-command syncdb 的使用(和修改)解决方案

Flask-Migrate 的作者 Miguel Grinberg 回复here 一个似乎与我的问题非常相似的问题。

我能找到的关于堆栈溢出的最接近的是 this,但它对我来说并没有太多地说明整个事情,而且答案从未被接受(我无法理解)工作,我试过几次)

供参考,这是我的 manage.py:

#!/usr/bin/env python

from flask.ext.script import Server, Manager, Shell
from flask.ext.migrate import Migrate, MigrateCommand


from app import app, db

manager = Manager(app)
manager.add_command('runserver', Server())
manager.add_command('shell', Shell(make_context=lambda: {
    'app': app,
    'db_session': db.session
}))

migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)

@manager.command
def syncdb():
    from social.apps.flask_app.default import models
    models.PSABase.metadata.create_all(db.engine)
    db.create_all()

if __name__ == '__main__':
    manager.run()

澄清一下,db init / migrate / upgrade 命令只会创建我的用户 table(显然还有迁移用户),但不会创建社交身份验证用户,而 syncdb 命令适用于 python-社交认证 tables.

我从 github 的回复中了解到 Flask-Migrate 不支持此功能,但我想知道 PSABase-table 中是否有 fiddle 的方法s 所以它们被发送到 Migrate 中的 db-object 拾取。

欢迎提出任何建议。

(另外,第一次post呃。我觉得我已经做了很多研究并尝试了很多解决方案,然后才最终来到这里post。如果我已经遗漏了 SO 指南中的一些明显内容,请不要犹豫,在私信中向我指出,我会很乐意帮忙)

问题是您有两组模型,每组都有不同的 SQLAlchemy 元数据对象。 PSA 的模型是直接从 SQLAlchemy 生成的,而您自己的模型是通过 Flask-SQLAlchemy 生成的。

Flask-Migrate 只能看到通过 Flask-SQLAlchemy 定义的模型,因为您给它的 db 对象只知道这些模型的元数据,它对这些其他 PSA 模型一无所知绕过了 Flask-SQLAlchemy。

是的,最终结果是每次生成迁移时,Flask-Migrate/Alembic 在数据库中找到这些 PSA 表并决定删除它们,因为它看不到它们的任何模型。

我认为您问题的最佳解决方案是配置 Alembic 以忽略某些表。为此,您可以使用存储在迁移目录中的 env.py 模块中的 include_object 配置。基本上,您将编写一个函数,Alembic 在生成迁移脚本时每次遇到新实体时都会调用该函数。当所讨论的对象是这些 PSA 表之一时,该函数将 return False,对于其他所有对象 True

更新:您在回复中包含的另一种选择是将两个元数据对象合并为一个,然后 Alembic 将同时检查您的应用程序和 PSA 中的模型。

我不反对将多个元数据对象合并为一个的技术,但我认为应用程序跟踪不属于您的模型中的迁移不是一个好主意。很多时候 Alembic 无法准确捕获迁移,因此您可能需要在应用生成的脚本之前对其进行小的更正。对于您的模型,您能够检测到这些有时会出现在迁移脚本中的不准确之处,但是当模型不是您的模型时,我认为您可能会遗漏一些东西,因为您对这些模型中的更改不够熟悉模型对 Alembic 生成的脚本进行很好的审查。

出于这个原因,我认为使用我建议的 include_object 配置将第三方模型排除在迁移之外是一个更好的主意。应改为根据第三方项目的说明迁移这些模型。

在 Miguel I got some new keywords to research. I ended up at a helpful github-page which had further references to, amongst others, the Alembic bitbucket site 的有用回答之后帮助很大。

最后我对我的 Alembic 迁移做了这个 env.py-file:

from sqlalchemy import engine_from_config, pool, MetaData

[...]

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from flask import current_app
config.set_main_option('sqlalchemy.url',
                       current_app.config.get('SQLALCHEMY_DATABASE_URI'))

def combine_metadata(*args):
    m = MetaData()
    for metadata in args:
        for t in metadata.tables.values():
            t.tometadata(m)
    return m

from social.apps.flask_app.default import models

target_metadata = combine_metadata(
    current_app.extensions['migrate'].db.metadata,
    models.PSABase.metadata)

这似乎非常完美。

我使用以下两个模型:-

一个使用 db 作为

db = SQLAlchemy()
app['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:' + POSTGRES_PASSWORD + '@localhost/Flask'
db.init_app(app)

class User(db.Model):
    pass

另一个以Base为

Base = declarative_base()
uri = 'postgresql://postgres:' + POSTGRES_PASSWORD + '@localhost/Flask'
engine = create_engine(uri)
metadata = MetaData(engine)
Session = sessionmaker(bind=engine)
session = Session()

class Address(Base):
    pass

由于您使用 db.Model 创建了用户,因此您可以在用户上使用 flask migrate 和 class 使用的地址 Base 处理从数据库中获取预先存在的 table。