如何使用flask-migrate与其他declarative_bases

时间:2016-02-03 23:39:54

标签: sqlalchemy flask-sqlalchemy python-social-auth flask-migrate

我正在尝试在Flask中实现python-social-auth。我在尝试同时解释大约4个教程和一本完整的Flask书时解决了大量的问题,并且觉得我已经通过Flask-migrate达到了一定的僵局。

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

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

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

我已经得到了这个解决方案,可以使用the python-social-auth Flask example

建议的manage.py命令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命令只创建我的用户表(显然是迁移),而不是社交认证用户表,而syncdb命令适用于python-social-auth表。 / p>

我从github的回复中了解到Flask-Migrate不支持这一点,但我想知道是否有办法在PSABase表中进行操作,以便它们被发送到Migrate的db-object选中。

欢迎任何建议。

(另外,第一次发布海报。我觉得我做了很多研究,并且在我最终来到这里之前尝试了很多解决方案。如果我错过了SO的指导方针中明显的内容,那就不要我会毫不犹豫地在私信中向我指出这一点,我很乐意帮忙)

3 个答案:

答案 0 :(得分:4)

在Miguel here的有用答案之后,我得到了一些新的关键词进行研究。我最终在a helpful github-page进一步引用了Alembic bitbucket site,其中包含了极大的帮助。

最后,我对我的Alembic迁移env.py文件执行了此操作:

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)

这看起来非常完美。

答案 1 :(得分:2)

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

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

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

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

更新:您在编写的响应中包含的另一个选项是将两个元数据对象合并为一个,然后Alembic将对您的应用程序和PSA中的模型进行检查。

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

出于这个原因,我认为最好使用我提议的include_object配置让第三方模型退出迁移。应该根据第三方项目的指示迁移这些模型。

答案 2 :(得分:0)

我使用以下两种模式: -

使用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创建了用户,因此您可以在User上使用flask migrate,并使用Base处理从数据库中获取预先存在的表的类。