在启动子线程时,Flask抛出'在请求上下文之外工作'

时间:2012-03-29 19:02:22

标签: python flask mongoengine

我正在尝试在Flask应用程序中的Python中启动一个新线程。我正在做由请求触发的后台工作,但我不需要等待完成工作来响应请求。

是否可以在此子威胁中将烧瓶请求设置为进入的请求?原因是,我们对DB的查询(mongoDB前面的mongoengine)的ACL依赖于请求的用户(它从flask的请求对象中获取)以查看他们是否有权访问这些对象,并且因为请求是在子线程中不可用。

任何想法都会非常感激。

这是我现在处理它的伪代码,但它无法正常工作。

@app.route('/my_endpoint', methods=['POST'])
def my_endpoint_handler():
    #do tracking in sub-thread so we don't hold up the page
    def handle_sub_view(req):
        from flask import request
        request = req
        # Do Expensive work
    thread.start_new_thread(handle_sub_view, (request))
    return "Thanks"

5 个答案:

答案 0 :(得分:58)

将您的主题代码包裹在test_request_context中,以便您有权访问context locals

@app.route('/my_endpoint', methods=['POST'])
def my_endpoint_handler():
    #do tracking in sub-thread so we don't hold up the page
    def handle_sub_view(req):
        with app.test_request_context():
            from flask import request
            request = req
            # Do Expensive work
    thread.start_new_thread(handle_sub_view, (request))
    return "Thanks"

编辑:值得指出的是,该主题将具有与原始请求不同的上下文。在生成线程之前,您需要提取任何有趣的请求数据,例如用户ID。然后,您可以使用ID在子线程中获取(不同的)用户对象。

答案 1 :(得分:30)

从版本0.10开始,支持这种方式:http://flask.pocoo.org/docs/api/#flask.copy_current_request_context

如果您希望before_request挂钩运行,则必须在已修饰函数内调用current_app.preprocess_request()

答案 2 :(得分:5)

您可以复制所需的信息并将其传递出去:

@app.route('/my_endpoint', methods=['POST'])
def my_endpoint_handler():
    #do tracking in sub-thread so we don't hold up the page
    def handle_sub_view(data):
        # Use the data in subprocess
    data = request.get_json()  # copy the data
    thread.start_new_thread(handle_sub_view, data)
    return "Thanks"

答案 3 :(得分:1)

正如@runfalk指出的那样,您需要使用@copy_current_request_context。这是一个工作代码段:

import threading

from flask import request, jsonify, copy_current_request_context


@app.route('/foo')
def get_foo():
    @copy_current_request_context
    def foo_main():
        # insert your code here
        print(request.url)

    threading.Thread(target=foo_main).start()

    return jsonify({'status': 'started'})

答案 4 :(得分:1)

更简洁的方法是使用Flask内置的执行器来包装应用上下文,请参见https://flask-executor.readthedocs.io/en/latest/