web-dev-qa-db-ja.com

Flaskサブスレッドを開始するときに「リクエストコンテキストの外で作業」を投げる

Python Flaskアプリケーション内で新しいスレッドを開始しようとしています。リクエストによってトリガーされるバックグラウンド作業を行っていますが、そうではありません。リクエストに応答するために作業が完了するまで待つ必要があります。

このサブ脅威のflaskリクエストを着信したリクエストに設定することは可能ですか?理由は、DB(mongoDBの前のmongoengine)に対するクエリのACLはリクエストのユーザー(フラスコのリクエストオブジェクトから取得します)がオブジェクトにアクセスできるかどうかを確認し、サブスレッドでリクエストが利用できないために爆発します。

どんな考えでも大歓迎です。

以下は、現在どのように処理しているかを示す擬似コードですが、動作していません。

@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"
48
MattoTodd

スレッドコードを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"

Edit:スレッドが元のリクエストとは異なるコンテキストを持つことに注意する価値があります。スレッドを生成する前に、ユーザーIDなどの興味深いリクエストデータを抽出する必要があります。次に、IDを使用して、サブスレッドで(異なる)ユーザーオブジェクトを取得できます。

61
Alex Morega

バージョン0.10以降、これを行うためのサポートされた方法があります。 http://flask.pocoo.org/docs/api/#flask.copy_current_request_context

_before_request_フックを実行する場合は、装飾された関数内でcurrent_app.preprocess_request()を呼び出す必要があります。

30
runfalk

目的の情報をコピーして渡すことができます。

@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"
5
vim

@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'})
1
jrc