问题描述
我想创建一个等待页面,我可以在执行一些长函数之前呈现此页面.完成后,它将返回到成功页面.这是一个简单的代码示例
@ app.route("/process",methods = ['GET','POST'])def process():使用app.app_context():数据= render_template('waiting.html')### ..做点长时间,必须等待.. ####例如睡几秒钟返回重定向(url_for('成功'))
发生的是,似乎未执行 data = render_template('waiting.html')
行.我看到的是黑屏一段时间(因为它是下面的处理功能).而且,完成后,它会按预期显示成功页面.
这似乎是一个简单的任务,之前在这里已经提出了许多问题.但是这些解决方案不适用于我.我的目标只是在执行任务之前显示模板.这样用户可以看到他们的请求正在进行中.
这是我尝试过的.
- https://stackoverflow.com/a/31831773/3054766
- https://stackoverflow.com/a/30382442/3054766
- https://stackoverflow.com/a/33944561/3054766
- 我在上一个项目中考虑了Celery.但是我发现对于这个简单的工作,它是完全不合格的.因此,我怀疑Celery是一个不错的选择.
如果我误解了Flask的概念,请告诉我.谢谢!
这里缺少两件事:
-
render_template
实际做什么 - HTTP流的工作方式
对于1来说答案很简单. render_template
将采用您要渲染的模板的路径以及您要用来渲染模板的上下文,并返回一个字符串.因此,它不会对请求做任何事情,它只会根据模板和上下文生成一个字符串(并且在您的代码示例中,它将将此呈现的字符串存储在 data
变量中)./p>
对于2,您需要了解的是,当用户向您的/process
路由发出请求时,它将期望一个响应,并且只有一个响应.
因此,您的路线需要决定要做出的响应:这是一个包含某些内容(例如某些HTML)的 200 OK
吗?或 301重定向
会指示用户的浏览器转到另一个页面?您不能同时收到两个响应,因为它违反了协议.
为了获得所需的体验,您将必须指示用户的浏览器向您的服务器发出多个请求.您可以使用几种策略,但是最简单的策略可能是使用Ajax.
这个想法是,您的初始路线只会呈现 waiting.html
页面,但是会有所不同.在页面中,您将添加一些JavaScript代码,这些JavaScript代码会向另一条路由发出Ajax请求,从而完成实际的长期运行工作.
所以现在您有两条路线:
@ app.route("/process",methods = ['GET','POST'])def process():如果request.method =='GET':返回render_template('waiting.html')如果request.method =='POST':### ..做点长时间,必须等待.. ####例如睡几秒钟返回完成"
(即使这只是一种方法,它们实际上是两条路由: GET/process
和 POST/process
)
页面中的JavaScript如下所示:
var request = new XMLHttpRequest();request.open('POST','/process');request.onload = function(){if(request.status === 200&&request.responseText ==='完成'){//长进程成功完成,重定向用户window.location ='/成功';} 别的 {//操作,我们收到了服务器的错误消息警报(出了点问题.");}};request.onerror = function(){//操作,尝试与服务器对话时出现错误警报(出了点问题.");};request.send();
(或者如果您使用的是 jQuery )
此操作将在后台向服务器发起另一个HTTP请求,这将启动该长时间运行的作业,并会收到文本 done
作为响应.发生这种情况时,它将把用户重定向到/success
页面.
现在,重要的是要记住,这非常简单,您需要注意一些事项:
-
这些Ajax请求中的每个请求都将阻止与服务器的连接,如果您使用的是gunicorn之类的服务器,则该服务器将为正式工作.因此,如果您的处理时间太长或有大量并发用户访问此路由,则服务器将非常过载.
-
您需要正确处理错误.在我的示例中,我总是返回
done
并带有200 OK
(这是默认状态码),但是如果处理中出现问题,您将返回有些不同,并且可能还会向用户显示一些错误消息.
I want to create a waiting page that I can render this page before executing some long function. And when it finishes, it will redirect to success page in return. This is an simple example of code
@app.route("/process", methods=['GET', 'POST'])
def process():
with app.app_context():
data = render_template('waiting.html')
### .. Do something long and have to wait .. ###
# e.g. sleep a few seconds
return redirect(url_for('success'))
What's happened is that the line data = render_template('waiting.html')
seems not executed. What I see is blank screen for a while (as it is processing function below). And, when it finishes, it shows success page as expected.
It seems to be a simple task that many questions have been asked here before. But those solutions are not working with me. My goal is just to make the template show up before doing a task. So that users will see that their requests are in progress.
Here are what I've tried.
- https://stackoverflow.com/a/31831773/3054766
- https://stackoverflow.com/a/30382442/3054766
- https://stackoverflow.com/a/33944561/3054766
- I considered Celery in my last project. But I found that it quite overqualified for this simple job. So I doubt that is Celery is a decent choice to choose.
Please kindly tell me if I misunderstand the concept of Flask.Thanks!
There are two things that you are missing here:
- what
render_template
actually does - how the HTTP flow works
For 1 the answer is quite simple. render_template
will take the path to the template you want to render and the context that you want to render the template with and return a string. So it will not do anything to the request, it will just generate a string based on the template and the context (and in your code sample, it will store this rendered string in the data
variable).
For 2 what you need to understand is that when an user makes a request to your /process
route it will expect a response, and only one response.
So your route needs to decide what to respond: will it be a 200 OK
with some content (some HTML for example)? or a 301 Redirect
that will instruct the user's browser to go to another page? You can't have both responses as it violates the protocol.
In order to achieve the experience you want you will have to instruct the user's browser to make multiple requests to your server. There are a few strategies you can use for this, but the simplest one is probably to use Ajax.
The idea is that your initial route will just render the waiting.html
page, but with a twist. In the page you will add some JavaScript code that makes an Ajax request to a different route that will do the actual long running job.
So now you have two routes:
@app.route("/process", methods=['GET', 'POST'])
def process():
if request.method == 'GET':
return render_template('waiting.html')
if request.method == 'POST':
### .. Do something long and have to wait .. ###
# e.g. sleep a few seconds
return 'done'
(even though it's only one method, they are essentially two routes: GET /process
and POST /process
)
The JavaScript in your page will look something like this:
var request = new XMLHttpRequest();
request.open('POST', '/process');
request.onload = function() {
if (request.status === 200 && request.responseText === 'done') {
// long process finished successfully, redirect user
window.location = '/success';
} else {
// ops, we got an error from the server
alert('Something went wrong.');
}
};
request.onerror = function() {
// ops, we got an error trying to talk to the server
alert('Something went wrong.');
};
request.send();
(or if you are using jQuery)
What this will do is it will initiate another HTTP request to your server in the background that will kick-off that long running job and will receive as response the text done
. When this happens, it will redirect the user to the /success
page.
Now, it's important to keep in mind that this is a very simple and you need to keep somethings in mind:
each one of these Ajax requests will block a connection to your server, which, if you are using something like gunicorn, would be a full worker. So if your processing takes too long or if you have a lot of concurrent users accessing this route your server will be very overloaded.
you need to handle errors properly. In my example I always return
done
with a200 OK
(which is the default status code), but if something goes wrong in the processing you would return something different and possibly some kind of error message to show to the user.
这篇关于在执行长函数之前,flask不是render_template的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!