我读过许多不同的博客,解释如何将Flask和Celery结合在一起。我也读过很多关于这个主题的问题。然而,已经好几天了,我仍然无法解决这个"RuntimeError:在应用程序上下文之外工作"的问题,我不知道在这一点上我能做些什么来解决这个问题。
该项目是这样完成的:
-
/应用
- __初始化__
- 模型
- 路线
- tfidf匹配
- cele_worker
- 配置
- wsgi
我实现了我的项目,以拥有本文中所做的Factory体系结构。然后,正如本文所解释的,已经完成了芹菜实现,以适应这个Factory体系结构。
HTTP请求在routes.py中处理,并将调用一个要在后台完成的芹菜任务。同时,该应用程序可以继续运行并做其他事情。它将发送一个带有另一个函数的POST HTTP,当芹菜任务完成时,它将发送芹菜结果的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
celey_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:usersemmaappdatalocalprogramspythonpython37librunpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "c:usersemmaappdatalocalprogramspythonpython37librunpy.py", line 85, in _run_code
exec(code, run_globals)
File "C:UsersEmmaAppDataLocalProgramsPythonPython37Scriptscelery.exe__main__.py", line 7, in <module>
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packagescelery__main__.py", line 16, in main
_main()
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packagescelerybincelery.py", line 322, in main
cmd.execute_from_commandline(argv)
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packagescelerybincelery.py", line 495, in execute_from_commandline
super(CeleryCommand, self).execute_from_commandline(argv)))
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packagescelerybinbase.py", line 289, in execute_from_commandline
argv = self.setup_app_from_commandline(argv)
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packagescelerybinbase.py", line 509, in setup_app_from_commandline
self.app = self.find_app(app)
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packagescelerybinbase.py", line 531, in find_app
return find_app(app, symbol_by_name=self.symbol_by_name)
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packagesceleryapputils.py", line 373, in find_app
sym = symbol_by_name(app, imp=imp)
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packagescelerybinbase.py", line 534, in symbol_by_name
return imports.symbol_by_name(name, imp=imp)
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packageskombuutilsimports.py", line 57, in symbol_by_name
module = imp(module_name, package=package, **kwargs)
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packagesceleryutilsimports.py", line 111, in import_from_cwd
return imp(module, package=package)
File "c:usersemmaappdatalocalprogramspythonpython37libimportlib__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:UsersEmmaPycharmProjectsAPG_Dialogflowcelery_worker.py", line 2, in <module>
from application.routes import tfidf
File "C:UsersEmmaPycharmProjectsAPG_Dialogflowapplicationroutes.py", line 21, in <module>
@app.before_first_request
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packageswerkzeuglocal.py", line 348, in __getattr__
return getattr(self._get_current_object(), name)
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packageswerkzeuglocal.py", line 307, in _get_current_object
return self.__local()
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packagesflaskglobals.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
,但这不起作用。我的应用程序实例应该以另一种方式导入,但我不知道是如何导入的
当烧瓶运行时:烧瓶运行我得到下面的Traceback
Traceback (most recent call last):
File "c:usersemmaappdatalocalprogramspythonpython37libsite-packagesflaskcli.py", line 240, in locate_app
__import__(module_name)
File "C:UsersEmmaPycharmProjectsAPG_Dialogflowwsgi.py", line 4, in <module>
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
File "C:UsersEmmaPycharmProjectsAPG_Dialogflowapplication__init__.py", line 28, in create_app
from . import routes
File "C:UsersEmmaPycharmProjectsAPG_Dialogflowapplicationroutes.py", line 6, in <module>
from wsgi import app
ImportError: cannot import name 'app' from 'wsgi' (C:UsersEmmaPycharmProjectsAPG_Dialogflowwsgi.py)
问题是您的代码中有以下内容:
from flask import current_app as app
然后在@app.route
和@app.before_first_request
中使用app
。
这是不正确的,Flask没有办法弄清楚你的应用程序实例是什么。您需要做的是使用实际的应用程序实例,而不是current_app
。
您可以在视图函数中使用current_app
,因为Flask为您设置了应用程序上下文,但您不能在应用程序的全局范围中使用它。
更新后更新答案:
您现在看到的是一个循环依赖问题。请注意最后一个stacktrace如何显示wsgi.py
,进入application/__init__.py
,然后进入application/routes.py
,最后返回到wsgi.py
。
对于Flask应用程序中的此问题,一个相当常见的解决方案是将application/__init__.py
文件中的from . import routes
一直移动到。文件底部。我相信这将消除循环依赖。