5

我正在尝试在 Flask 中实现 python-social-auth。我在尝试同时解释大约 4 个教程和一本完整的 Flask-book 的同时解决了很多问题,并且觉得我在 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-drop,但我可以想象它是那些在某一时刻会被遗忘的事情之一,突然之间我不再有 OAuth 关系了。

正如python-social-auth Flask 示例所建议的那样,我已经得到了这个解决方案来使用(和修改)manage.py-command syncdb

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

我能找到的最接近堆栈溢出的是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 表。

我从 github 响应中了解到 Flask-Migrate 不支持此功能,但我想知道是否有办法在 PSABase-tables 中摆弄,以便它们被发送到 Migrate 的 db-object 拾取。

欢迎任何建议。

(另外,第一次发帖。我觉得我已经做了很多研究并尝试了很多解决方案,然后才终于来到这里发布。如果我错过了 SO 指南中明显的内容,请不要犹豫在私信中向我指出这一点,我很乐意效劳)

4

3 回答 3

4

在 Miguel 给出有用的回答后,得到了一些新的关键字来研究。我最终进入了一个有用的 github 页面,其中进一步引用了Alembic bitbucket 站点等,该站点提供了极大的帮助。

最后,我对我的 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)

这似乎绝对完美。

于 2016-02-04T09:09:13.340 回答
2

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

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

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

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

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

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

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

于 2016-02-04T02:47:19.400 回答
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 类地址来处理从数据库中获取预先存在的表。

于 2017-05-06T13:20:15.957 回答