Flask 的上下文堆栈的目的是什么? [英] What is the purpose of Flask's context stacks?

查看:34
本文介绍了Flask 的上下文堆栈的目的是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用请求/应用程序上下文有一段时间了,但没有完全理解它是如何工作的,或者为什么它是这样设计的.当涉及到请求或应用程序上下文时,堆栈"的目的是什么?这是两个独立的堆栈,还是它们都属于一个堆栈?请求上下文是推送到堆栈上,还是堆栈本身?我可以在彼此之上推送/弹出多个上下文吗?如果是这样,我为什么要这样做?

I've been using the request/application context for some time without fully understanding how it works or why it was designed the way it was. What is the purpose of the "stack" when it comes to the request or application context? Are these two separate stacks, or are they both part of one stack? Is the request context pushed onto a stack, or is it a stack itself? Am I able to push/pop multiple contexts on top of eachother? If so, why would I want to do that?

很抱歉所有的问题,但在阅读了请求上下文和应用程序上下文的文档后,我仍然感到困惑.

Sorry for all the questions, but I'm still confused after reading the documentation for Request Context and Application Context.

推荐答案

多个应用

在您意识到 Flask 可以有多个应用程序之前,应用程序上下文(及其目的)确实令人困惑.想象一下你想让一个 WSGI Python 解释器运行多个 Flask 应用程序的情况.我们在这里不是在谈论蓝图,而是在谈论完全不同的 Flask 应用程序.

Multiple Apps

The application context (and its purpose) is indeed confusing until you realize that Flask can have multiple apps. Imagine the situation where you want to have a single WSGI Python interpreter run multiple Flask application. We're not talking Blueprints here, we're talking entirely different Flask applications.

您可以将其设置为类似于 Flask 文档应用程序调度"部分 例子:

You might set this up similar to the Flask documentation section on "Application Dispatching" example:

from werkzeug.wsgi import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend

application = DispatcherMiddleware(frontend, {
    '/backend':     backend
})

请注意,前端"和后端"创建了两个完全不同的 Flask 应用程序.换句话说,Flask(...) 应用程序构造函数被调用了两次,创建了 Flask 应用程序的两个实例.

Notice that there are two completely different Flask applications being created "frontend" and "backend". In other words, the Flask(...) application constructor has been called twice, creating two instances of a Flask application.

当您使用 Flask 时,您通常最终会使用全局变量来访问各种功能.例如,您的代码可能读取...

When you are working with Flask, you often end up using global variables to access various functionality. For example, you probably have code that reads...

from flask import request

然后,在查看过程中,您可能会使用request 来访问当前请求的信息.显然,request 不是一个普通的全局变量;实际上,它是一个 context local 值.换句话说,幕后有一些魔法说当我调用 request.path 时,从 request 对象中获取 path 属性的当前请求."两个不同的请求对于 request.path 会有不同的结果.

Then, during a view, you might use request to access the information of the current request. Obviously, request is not a normal global variable; in actuality, it is a context local value. In other words, there is some magic behind the scenes that says "when I call request.path, get the path attribute from the request object of the CURRENT request." Two different requests will have a different results for request.path.

事实上,即使你用多个线程运行 Flask,Flask 也足够聪明,可以保持请求对象的隔离.这样一来,两个线程就有可能分别处理不同的请求,同时调用 request.path 并为各自的请求获取正确的信息.

In fact, even if you run Flask with multiple threads, Flask is smart enough to keep the request objects isolated. In doing so, it becomes possible for two threads, each handling a different request, to simultaneously call request.path and get the correct information for their respective requests.

所以我们已经看到 Flask 可以在同一个解释器中处理多个应用程序,而且由于 Flask 允许您使用上下文本地"全局变量的方式,必须有某种机制来确定当前"request 是(为了做诸如request.path之类的事情).

So we've already seen that Flask can handle multiple applications in the same interpreter, and also that because of the way that Flask allows you to use "context local" globals there must be some mechanism to determine what the "current" request is (in order to do things such as request.path).

将这些想法放在一起,Flask 必须有某种方法来确定当前"应用程序是什么也应该是有意义的!

Putting these ideas together, it should also make sense that Flask must have some way to determine what the "current" application is!

您可能还有类似以下的代码:

You probably also have code similar to the following:

from flask import url_for

与我们的 request 示例一样,url_for 函数具有依赖于当前环境的逻辑.然而,在这种情况下,很明显逻辑在很大程度上取决于哪个应用程序被视为当前"应用程序.在上面显示的前端/后端示例中,前端"和后端"应用程序都可以有一个/login"路由,因此 url_for('/​​login') 应该返回不同的内容,具体取决于如果视图正在处理前端或后端应用的请求.

Like our request example, the url_for function has logic that is dependent on the current environment. In this case, however, it is clear to see that the logic is heavily dependent on which app is considered the "current" app. In the frontend/backend example shown above, both the "frontend" and "backend" apps could have a "/login" route, and so url_for('/login') should return something different depending on if the view is handling the request for the frontend or backend app.

当涉及到请求或请求时,堆栈"的目的是什么?应用上下文?

What is the purpose of the "stack" when it comes to the request or application context?

来自请求上下文文档:

因为请求上下文在内部维护为一个堆栈,你可以多次推送和弹出.这个实现起来很方便诸如内部重定向之类的事情.

Because the request context is internally maintained as a stack you can push and pop multiple times. This is very handy to implement things like internal redirects.

换句话说,即使您通常在这些当前"请求或当前"应用程序堆栈中会有 0 或 1 个项目,但您可能会有更多.

In other words, even though you typically will have 0 or 1 items on these stack of "current" requests or "current" applications, it is possible that you could have more.

给出的示例是您的请求将返回内部重定向"的结果.假设用户请求 A,但您想返回给用户 B.在大多数情况下,您向用户发出重定向,并将用户指向资源 B,这意味着用户将运行第二个请求来获取 B.A处理这种情况的稍微不同的方法是进行内部重定向,这意味着在处理 A 时,Flask 会向自己发出一个新的资源 B 请求,并将第二个请求的结果用作用户原始请求的结果.

The example given is where you would have your request return the results of an "internal redirect". Let's say a user requests A, but you want to return to the user B. In most cases, you issue a redirect to the user, and point the user to resource B, meaning the user will run a second request to fetch B. A slightly different way of handling this would be to do an internal redirect, which means that while processing A, Flask will make a new request to itself for resource B, and use the results of this second request as the results of the user's original request.

这是两个独立的堆栈,还是同一个堆栈的一部分?

Are these two separate stacks, or are they both part of one stack?

它们是两个独立的堆栈.然而,这是一个实现细节.更重要的不是有一个堆栈,而是您可以随时获取当前"应用程序或请求(堆栈顶部)这一事实.

They are two separate stacks. However, this is an implementation detail. What's more important is not so much that there is a stack, but the fact that at any time you can get the "current" app or request (top of the stack).

请求上下文是压入栈中,还是栈本身?

Is the request context pushed onto a stack, or is it a stack itself?

请求上下文"是请求上下文堆栈"中的一项.与应用上下文"和应用上下文堆栈"类似.

A "request context" is one item of the "request context stack". Similarly with the "app context" and "app context stack".

我可以在彼此之上推送/弹出多个上下文吗?如果是这样,我为什么要这样做?

Am I able to push/pop multiple contexts on top of eachother? If so, why would I want to do that?

在 Flask 应用程序中,您通常不会这样做.您可能想要的一个示例是内部重定向(如上所述).但是,即使在这种情况下,您最终也可能会让 Flask 处理新请求,因此 Flask 会为您完成所有推送/弹出操作.

In a Flask application, you typically would not do this. One example of where you might want to is for an internal redirect (described above). Even in that case, however, you would probably end up having Flask handle a new request, and so Flask would do all of the pushing/popping for you.

但是,在某些情况下,您希望自己操作堆栈.

However, there are some cases where you'd want to manipulate the stack yourself.

人们遇到的一个典型问题是他们使用 Flask-SQLAlchemy 扩展来设置 SQL 数据库和模型定义,使用如下所示的代码...

One typical problem people have is that they use the Flask-SQLAlchemy extension to set up a SQL database and model definition using code something like what is shown below...

app = Flask(__name__)
db = SQLAlchemy() # Initialize the Flask-SQLAlchemy extension object
db.init_app(app)

然后他们在应该从 shell 运行的脚本中使用 appdb 值.例如,setup_tables.py"脚本...

Then they use the app and db values in a script that should be run from the shell. For example, a "setup_tables.py" script...

from myapp import app, db

# Set up models
db.create_all()

在这种情况下,Flask-SQLAlchemy 扩展知道 app 应用程序,但是在 create_all() 期间它会抛出一个错误,抱怨没有应用程序上下文.这个错误是有道理的;在运行 create_all 方法时,你从来没有告诉 Flask 它应该处理什么应用程序.

In this case, the Flask-SQLAlchemy extension knows about the app application, but during create_all() it will throw an error complaining about there not being an application context. This error is justified; you never told Flask what application it should be dealing with when running the create_all method.

您可能想知道为什么当您在视图中运行类似的函数时最终不需要这个 with app.app_context() 调用.原因是 Flask 在处理实际 Web 请求时已经为您处理了应用程序上下文的管理.问题实际上只出现在这些视图函数(或其他此类回调)之外,例如在一次性脚本中使用模型时.

You might be wondering why you don't end up needing this with app.app_context() call when you run similar functions in your views. The reason is that Flask already handles the management of the application context for you when it is handling actual web requests. The problem really only comes up outside of these view functions (or other such callbacks), such as when using your models in a one-off script.

解决方案是自己推送应用上下文,可以通过做...

The resolution is to push the application context yourself, which can be done by doing...

from myapp import app, db

# Set up models
with app.app_context():
    db.create_all()

这将推送一个新的应用程序上下文(使用 app 的应用程序,记住可能有多个应用程序).

This will push a new application context (using the application of app, remember there could be more than one application).

您想要操作堆栈的另一种情况是用于测试.您可以创建一个处理请求的单元测试并检查结果:

Another case where you would want to manipulate the stack is for testing. You could create a unit test that handles a request and you check the results:

import unittest
from flask import request

class MyTest(unittest.TestCase):
    def test_thing(self):
        with app.test_request_context('/?next=http://example.com/') as ctx:
            # You can now view attributes on request context stack by using `request`.

        # Now the request context stack is empty

这篇关于Flask 的上下文堆栈的目的是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆