Flask中的return语句后如何触发函数

时间:2018-11-30 11:47:59

标签: python python-3.x flask

我有2个功能。

第一个函数将接收到的数据存储在一个列表中,第二个函数将数据写入一个csv文件中。

我正在使用Flask。每当调用Web服务时,它将存储数据并发送对它的响应,一旦发送响应,它将触发第二个功能。

我的代码

from flask import Flask, flash, request, redirect, url_for, session
import json

app = Flask(__name__)

arr = []

@app.route("/test", methods=['GET','POST'])
def check():
    arr.append(request.form['a'])
    arr.append(request.form['b'])
    res = {'Status': True}
    return json.dumps(res)

def trigger():
    df = pd.DataFrame({'x': arr})
    df.to_csv("docs/xyz.csv", index=False)
    return 

显然第二个函数没有被调用。

有没有办法做到这一点?

P.S:我的现实生活的问题有所不同,其中trigger函数很耗时,我不希望用户等待它完成执行。

5 个答案:

答案 0 :(得分:1)

我实际上正在处理我这边的另一个有趣的案例,在该案例中,我将工作传递给了一个python worker,该python worker将工作发送到redis队列。有一些很棒的博客在Flask上使用redis,您基本上需要确保redis正在运行(能够在端口6379上连接)

工人看起来像这样:

import os

import redis
from rq import Worker, Queue, Connection

listen = ['default']

redis_url = os.getenv('REDISTOGO_URL', 'redis://localhost:6379')

conn = redis.from_url(redis_url)

if __name__ == '__main__':
    with Connection(conn):
        worker = Worker(list(map(Queue, listen)))
        worker.work()

在我的示例中,我有一个查询数据库使用情况的函数,由于它可能是一个漫长的过程,因此我将其传递给工作程序(作为单独的脚本运行)

def post(self):

    data = Task.parser.parse_args()

    job = q.enqueue_call(
        func=migrate_usage, args=(my_args),
        result_ttl=5000
    )
    print("Job ID is: {}".format(job.get_id()))
    job_key = job.get_id()

    print(str(Job.fetch(job_key, connection=conn).result))

    if job:
        return {"message": "Job : {} added to queue".format(job_key)}, 201

归功于以下文章:

https://realpython.com/flask-by-example-implementing-a-redis-task-queue/#install-requirements

答案 1 :(得分:1)

一种解决方案是让后台线程监视队列。您将csv数据放入队列中,后台线程将使用它。您可以在第一个请求之前启动这样的线程:

import threading
from multiprocessing import Queue

class CSVWriterThread(threading.Thread):
    def __init__(self, *args, **kwargs):
        threading.Thread.__init__(self, *args, **kwargs)
        self.input_queue = Queue()

    def send(self, item):
        self.input_queue.put(item)

    def close(self):
        self.input_queue.put(None)
        self.input_queue.join()

    def run(self):
        while True:
            csv_array = self.input_queue.get()
            if csv_array is None:
                break

            # Do something here ...
            df = pd.DataFrame({'x': csv_array})
            df.to_csv("docs/xyz.csv", index=False)


            self.input_queue.task_done()
            time.sleep(1)
        # Done
        self.input_queue.task_done()
        return

@app.before_first_request
def activate_job_monitor():
    thread = CSVWriterThread()
    app.csvwriter = thread
    thread.start()

然后在返回的代码中将消息放入队列中

@app.route("/test", methods=['GET','POST'])
def check():
    arr.append(request.form['a'])
    arr.append(request.form['b'])
    res = {'Status': True}
    app.csvwriter.send(arr)
    return json.dumps(res)

答案 2 :(得分:1)

P.S:我的现实生活的问题有所不同,其中触发功能很耗时,我不希望用户等待它完成执行。

考虑使用celery来解决您要解决的问题。来自文档:

  

Celery是一个简单,灵活且可靠的分布式系统,可以处理大量消息,同时为操作提供维护该系统所需的工具。

我建议您按照here的说明将芹菜与烧瓶应用程序集成。这样,您的trigger方法将成为一项简单的芹菜任务,您可以执行它而不必担心响应时间长。

答案 3 :(得分:0)

您可以尝试使用流式传输。请参见下一个示例:

import time
from flask import Flask, Response

app = Flask(__name__)

@app.route('/')
def main():
    return '''<div>start</div>
    <script>
        var xhr = new XMLHttpRequest();
        xhr.open('GET', '/test', true);
        xhr.onreadystatechange = function(e) {
            var div = document.createElement('div');
            div.innerHTML = '' + this.readyState + ':' + this.responseText;
            document.body.appendChild(div);
        };
        xhr.send();
    </script>
    '''

@app.route('/test')
def test():
    def generate():
        app.logger.info('request started')
        for i in range(5):
            time.sleep(1)
            yield str(i)
        app.logger.info('request finished')
        yield ''
    return Response(generate(), mimetype='text/plain')

if __name__ == '__main__':
    app.run('0.0.0.0', 8080, True)

此示例中的所有魔术都是使用genarator进行的,您可以在其中执行一些工作并生成空数据以结束流后开始响应数据。

有关详细信息,请查看http://flask.pocoo.org/docs/patterns/streaming/

答案 4 :(得分:0)

您可以通过结合 after_this_request 和 response.call_on_close 来推迟具有有限上下文的路由特定操作。请注意,请求和响应上下文将不可用,但路由函数上下文仍然可用。因此,您需要将需要的任何请求/响应数据复制到本地变量中以进行延迟访问。

我将您的数组移动到本地 var 以显示函数上下文是如何保留的。您可以将 csv write 函数更改为 append,这样您就不会无休止地将数据推送到内存中。

from flask import Flask, flash, request, redirect, url_for, session
import json

app = Flask(__name__)

@app.route("/test", methods=['GET','POST'])
def check():
    arr = []
    arr.append(request.form['a'])
    arr.append(request.form['b'])
    res = {'Status': True}

    @flask.after_this_request
    def add_close_action(response):
        @response.call_on_close
        def process_after_request():
            df = pd.DataFrame({'x': arr})
            df.to_csv("docs/xyz.csv", index=False)
        return response
    return json.dumps(res)