【问题标题】:How to use flask-migrate with other declarative_bases如何将 flask-migrate 与其他 declarative_bases 一起使用
【发布时间】:2023-03-03 04:35:01
【问题描述】:

我正在尝试在 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 关系了。

按照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 命令只创建我的用户表(显然还有迁移表),而不是社交身份验证表,而 syncdb 命令适用于 python-social-auth 表。

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

欢迎提出任何建议。

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

【问题讨论】:

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


    【解决方案1】:

    在 Miguel here 提供有用的回答后,我得到了一些新的关键字来研究。我最终找到了a helpful github-page,其中进一步提到了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)
    

    这似乎工作得非常完美。

    【讨论】:

    • 我确认这应该是本问答中接受的答案。我一直无助地需要删除升级/降级迁移文件中所有社交身份验证表的删除/创建语句。进行上述修复后,升级/降级最初都将为空。完美的解决方案:)
    【解决方案2】:

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

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

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

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

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

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

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

    【讨论】:

    • 谢谢你的回答,米格尔。对,我几乎完全忘记了 Alembic。我不能跳过 Flask-Migrate 并直接使用它吗?在我看来,它可能被欺骗认为这是一个多数据库设置(从 Alembics 的角度来看,它基本上就是这样)。
    • 嗯,不确定使用 Alembic 是否会得到不同的结果。如果您设置了 multidb 配置(您也可以使用 Flask-Migrate 进行设置,顺便说一句),那么问题将出现在双方。常规数据库的迁移仍会尝试删除 PSA 模型,但除此之外,PSA 端的迁移也会尝试删除您的表。避免这种混乱的一种方法是使用两个单独的数据库,然后一侧的表对另一侧不可见。
    • 啊,是的,它们会相互矛盾。使用单独的数据库不会太糟糕,但我认为在故障排除期间用户很容易错过。我想我可以自己实现社交身份验证的东西。如此接近终点线让我有点难过。除了必须进行的迁移之外,这一切都在工作,这让我不得不再次将其全部撕掉并重新开始:P
    • include_object 选项有什么问题?通过将 PSA 模型排除在迁移评估器之外,这应该可以解决您的问题。
    • 通过将它们排除在外,我不会让自己暴露于无法迁移的 PSA 模型的更改中,还是我宁愿通过包含它们来使自己面临风险?既然我已经正确包含了 PSA 模型的更改(我自己的答案),那么迁移者不会检测到它们吗?
    【解决方案3】:

    我使用以下两种模型:-

    使用db as的一个

    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 创建了用户,因此您可以在用户和使用的类地址上使用烧瓶迁移 Base 来处理从数据库中获取预先存在的表。

    【讨论】:

      猜你喜欢
      • 2014-08-16
      • 2016-08-10
      • 1970-01-01
      • 2018-05-22
      • 1970-01-01
      • 2021-07-26
      • 2017-07-24
      • 1970-01-01
      • 2014-08-05
      相关资源
      最近更新 更多