当散布Flask模型时,引发了RuntimeError: 'application not registered on db'错误。

71

我正在通过分散模型和蓝图来重构我的Flask应用程序,但是我遇到了运行时错误。

def create_app():
    app = flask.Flask("app")
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
    app.register_blueprint(api)
    db.init_app(app)
    db.create_all()
    return app

我有以下问题(示例项目托管在此处:https://github.com/chfw/sample):

Traceback (most recent call last):
  File "application.py", line 17, in <module>
    app = create_app()
  File "application.py", line 12, in create_app
    db.create_all()
  File "\AppData\Roaming\Python\Python27\site-packages\flask_sqlalchemy\__init__.py", line 856, in create_all
    self._execute_for_all_tables(app, bind, 'create_all')
  File "\AppData\Roaming\Python\Python27\site-packages\flask_sqlalchemy\__init__.py", line 836, in _execute_for_all_tables
    app = self.get_app(app)
  File "\AppData\Roaming\Python\Python27\site-packages\flask_sqlalchemy\__init__.py", line 809, in get_app
    raise RuntimeError('application not registered on db 
           'RuntimeError: application not registered on db 
            instance and no application bound to current context

我对这个主题进行了研究。在这里建议使用重新构建:

Flask-SQLAlchemy import/context issue

同样的问题在这里提出:

http://flask.pocoo.org/mailinglist/archive/2010/8/30/sqlalchemy-init-app-problem/#b1c3beb68573efef4d6e571ebc68fa0b

上面的主题(2010)建议使用以下hack方法:

    app.register_blueprint(api)
    db.app=app #<------------<<
    db.init_app(app)

有人知道如何正确地做吗?你是如何解决的?

谢谢

2个回答

143
这与Flask的应用上下文有关。当使用db.init_app(app)初始化时,Flask-SQLAlchemy不知道哪个应用程序是“当前”应用程序(请记住,Flask允许在同一解释器中使用多个应用程序)。您可以在同一进程中使用多个应用程序来使用相同的SQLAlchemy实例,并且Flask-SQLAlchemy需要知道哪一个是“当前”应用程序(由于Flask的上下文本地性质)。如果您需要在运行时执行此操作,则必须显式指定所有调用的“当前”应用程序。您可以通过更改代码以使用with app.app_context()块来实现此目的:
def create_app():
    app = flask.Flask("app")
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
    app.register_blueprint(api)
    db.init_app(app)
    with app.app_context():
        # Extensions like Flask-SQLAlchemy now know what the "current" app
        # is while within this block. Therefore, you can now run........
        db.create_all()

    return app

如果您正在编写需要应用上下文的独立脚本,您可以在开头推送上下文,而不是将所有内容放入with块中。
create_app().app_context().push()

如果您为Flask的cli编写命令,该命令将自动访问上下文。

好的,这个可以运行。现在我不会再遇到异常了。但是下一个问题是:“应用程序未在数据库上注册” -> RuntimeError:应用程序未在数据库实例上注册,且当前上下文中没有绑定任何应用程序。 - chfw
2
这里和添加 db.app = app 有什么区别? - user805981
1
@user805981 这种方法可能有效,但不太干净。首先,您正在破坏封装性,因为 SQLAlchemy 上的 app 属性并不是公共设计的。当该对象更改其使用的应用程序时,该对象可能需要执行其他操作。其次,app_context 不仅仅是更改 Flask-SQLAlchemy;它实际上告诉 Flask 更改当前应用程序。因此,您可能需要使用它来处理各种 Flask API 或与所有其他 Flask 扩展一起使用应用程序。 - Mark Hildreth
2
@MarkHildreth 我也遇到了类似的问题,celery定期任务不知道上下文。即使使用了你的更新,我的唯一解决方法是执行db.app=app。我的定期应用有什么问题导致它不知道app_context在哪里吗?我的问题链接:https://dev59.com/wo3da4cB1Zd3GeqP1YpO 谢谢! - user805981

7
马克的回答很好,对我很有帮助。然而,另一种方法是在一个被@ app.before_first_request修饰的函数中运行依赖于应用上下文的代码。详见http://flask.pocoo.org/docs/0.10/appcontext/以获取更多信息。事实上,这正是我最终采取的方式,主要是因为我想能够在Flask之外调用初始化代码,我是通过这种方式来处理的。
在我的情况下,我想要测试SQLAlchemy模型作为纯SQLAlchemy模型,而不使用Flask-SQLAlchemy,尽管下面的db只是(Flask) SQLAlchemy db。
@app.before_first_request
def recreate_test_databases(engine = None, session = None):
  if engine == None:
    engine = db.engine
  if session == None:
    session = db.session

  Base.metadata.drop_all(bind=engine)
  Base.metadata.create_all(bind=engine)
  # Additional setup code

John,你能把重复的答案合并到这个里面(因为这个有赞成票),然后删除另一个吗? - KobeJohn
删除了,无需合并。 - John Lockwood
有没有关于应该放置 @app.before_first_request 函数的文件的建议? - AlexLordThorsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接