RuntimeError: Working outside of application context with Celery and Flask in Python

RuntimeError: Working outside of application context with Celery and Flask in Python

我读过许多解释如何结合使用 Flask 和 Celery 的不同博客。我还阅读了大量有关该主题的堆栈问题。然而,已经好几天了,我还是没能解决这个问题"RuntimeError: Working outside of application context",我不知道现在我能做些什么来解决这个问题。

项目是这样完成的:

我实现了我的项目以拥有工厂架构,就像在 article. Then, the celery implementation has been done to fit this Factory architecture as explained on this article 上所做的那样。

HTTP 请求在 routes.py 中处理,将调用一个 celery 任务在后台完成。与此同时,该应用程序可以继续 运行 并执行其他操作。它将发送带有另一个功能的 POST HTTP,当 celery 任务完成时,它将发送 celery 结果的 POST HTTP。

routes.py包含以下内容,我已将其简化为重要部分:

from flask import request, make_response, jsonify, copy_current_request_context
from flask import current_app as app
from application import tfidf_matching
import time
from application.models import db, Status
from app import cel

def snooze(maxTime):
    ...


@app.before_first_request
def default_values():
    ...

@cel.task
def tfidf(question):
    with app.app_context:
        answer = tfidf_matching.getMatchingSentence(question)
        ...
        return answer

@app.route('/webhook', methods=['GET', 'POST'])
def webhook():
    req = request.get_json(force=True)
    ...
    if ... :
        user_input = req.get('queryResult').get('queryText')
        answer = tfidf.apply_async(args=[user_input], expires=60)
        ...
    snooze(4)
    response = {'followupEventInput': {'name': 'snooze'}}
    if ...:
        response = {'fulfillmentText': answer}
    return make_response(jsonify(response))

我尝试使用@copy_current_request_context 和 app.app_context 来解决其他堆栈溢出问题中建议的错误,但没有成功。

Celery 使用以下命令启动:

celery worker -A celery_worker.cel --loglevel=info

celery_worker.py 包含以下内容:

import os
from app.routes import tfidf
from application import cel, create_app

app = create_app(os.getenv('FLASK_CONFIG') or 'default')
app.app_context().push()

我不确定是否有必要,但以防万一我还添加了 init 和 wsgi 内容。模型是一个 SQLAlchemy 数据库,这似乎不相关,所以我不会添加这部分。

__ init__.py 包含以下内容:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from celery import Celery
from config import config, Config

db = SQLAlchemy()

cel = Celery(__name__, broker=Config.broker_url, backend=Config.result_backend)


def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    db.init_app(app)
    app.config.update(
        broker_url='redis://localhost:6379',
        result_backend='redis://localhost:6379',
        SQLALCHEMY_DATABASE_URI='postgresql://postgres:APG@localhost:5432/Dialogflow',
        SQLALCHEMY_TRACK_MODIFICATIONS='None'
    )
    cel.conf.update(
        result_expires=3600,
    )
    cel.conf.update(app.config)
    with app.app_context():
        from . import routes
        db.create_all()
        return app

并且 wsgi.py 包含以下内容:

import os
from application import create_app

app = create_app(os.getenv('FLASK_CONFIG') or 'default')

if __name__ == "__main__":
    app.run(host='0.0.0.0')

最后,这是堆栈跟踪:

    Traceback (most recent call last):
  File "c:\users\emma\appdata\local\programs\python\python37\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Users\Emma\AppData\Local\Programs\Python\Python37\Scripts\celery.exe\__main__.py", line 7, in <module>
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\__main__.py", line 16, in main
    _main()
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\bin\celery.py", line 322, in main
    cmd.execute_from_commandline(argv)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\bin\celery.py", line 495, in execute_from_commandline
    super(CeleryCommand, self).execute_from_commandline(argv)))
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\bin\base.py", line 289, in execute_from_commandline
    argv = self.setup_app_from_commandline(argv)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\bin\base.py", line 509, in setup_app_from_commandline
    self.app = self.find_app(app)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\bin\base.py", line 531, in find_app
    return find_app(app, symbol_by_name=self.symbol_by_name)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\app\utils.py", line 373, in find_app
    sym = symbol_by_name(app, imp=imp)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\bin\base.py", line 534, in symbol_by_name
    return imports.symbol_by_name(name, imp=imp)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\kombu\utils\imports.py", line 57, in symbol_by_name
    module = imp(module_name, package=package, **kwargs)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\utils\imports.py", line 111, in import_from_cwd
    return imp(module, package=package)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "C:\Users\Emma\PycharmProjects\APG_Dialogflow\celery_worker.py", line 2, in <module>
    from application.routes import tfidf
  File "C:\Users\Emma\PycharmProjects\APG_Dialogflow\application\routes.py", line 21, in <module>
    @app.before_first_request
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\werkzeug\local.py", line 348, in __getattr__
    return getattr(self._get_current_object(), name)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\werkzeug\local.py", line 307, in _get_current_object
    return self.__local()
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\flask\globals.py", line 52, in _find_app
    raise RuntimeError(_app_ctx_err_msg)
RuntimeError: Working outside of application context.

This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context().  See the
documentation for more information.

编辑

按照 Miguel 的建议更改 from flask import current_app as app,错误就消失了。

我在 routes.py 中将导入更改为 from wsgi import app,但这不起作用。我的应用程序实例应该以另一种方式导入,但我不知道如何导入

当 运行ning flask with : flask 运行 我得到以下回溯

Traceback (most recent call last):
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\flask\cli.py", line 240, in locate_app
    __import__(module_name)
  File "C:\Users\Emma\PycharmProjects\APG_Dialogflow\wsgi.py", line 4, in <module>
    app = create_app(os.getenv('FLASK_CONFIG') or 'default')
  File "C:\Users\Emma\PycharmProjects\APG_Dialogflow\application\__init__.py", line 28, in create_app
    from . import routes
  File "C:\Users\Emma\PycharmProjects\APG_Dialogflow\application\routes.py", line 6, in <module>
    from wsgi import app
ImportError: cannot import name 'app' from 'wsgi' (C:\Users\Emma\PycharmProjects\APG_Dialogflow\wsgi.py)

问题是你的代码中有这个:

from flask import current_app as app

然后在 @app.route@app.before_first_request 中使用 app

这是不正确的,Flask 无法确定您的应用程序实例是什么。您需要做的是使用真实的应用程序实例而不是 current_app.

您可以在视图函数中使用 current_app,因为 Flask 会为您设置应用程序上下文,但您不能在应用程序的全局范围内使用它。

更新后更新到答案:

你现在怎么循环依赖的问题。请注意最后的堆栈跟踪如何显示 wsgi.py,进入 application/__init__.py,然后进入 application/routes.py,最后返回 wsgi.py.

Flask 应用程序中此问题的一个相当常见的解决方案是将 application/__init__.py 文件中的 from . import routes 一直移动到 .文件的底部。我相信这将消除循环依赖。