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

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

问题描述

我一直在使用请求/应用程序上下文一段时间,却没有完全理解它是如何工作的,或者为什么它的设计方式如此。当涉及到请求或应用程序上下文时,堆栈的目的是什么?这两个单独的堆栈,还是它们都是一个堆栈的一部分?请求上下文是否被压入堆栈,还是自己是堆栈?我能够在彼此之上推送/弹出多个上下文吗?如果是这样,我为什么要这样做?



对所有问题抱歉,但是在阅读请求上下文和应用程序上下文的文档后,我仍然感到困惑。 / b>

解决方案多个应用程序

应用程序上下文(及其目的)是确实令人困惑,直到你意识到Flask可以有多个应用程序。想象一下你想要一个WSGI Python解释器运行多个Flask应用程序的情况。我们不是在说Blueprints,而是说完全不同的Flask应用程序。



您可以设置类似于 Flask文档Application Dispatching部分 from werkzeug.wsgi import DispatcherMiddleware $ b $ from frontend_app导入应用程序作为前端
from backend_app导入应用程序作为后端

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

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

上下文



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

 来自瓶子导入请求

然后,在视图中,您可以使用 request 来访问当前请求的信息。显然, request 不是一个普通的全局变量,实际上,这是一个上下文本地值。换句话说,在幕后有一些魔术说:当我调用 request.path 时,得到路径属性来自CURRENT请求的请求对象。两个不同的请求对于 request.path

有不同的结果。事实上,即使你运行Flask多个线程,Flask足够聪明,可以保持请求对象的隔离。在这种情况下,两个线程(每个处理不同的请求)可以同时调用 request.path 并获得正确的信息,以满足各自的请求。



把它放在一起



所以我们已经看到Flask可以在同一个解释器中处理多个应用程序, Flask允许你使用上下文本地全局变量的方式必须有一些机制来确定当前请求是什么(为了执行诸如请求将这些想法放在一起,Flask必须有一些方法来确定当前应用程序

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

  from flask import url_for 

像我们的请求例如, url_for 函数具有依赖于当前的逻辑 环境。然而,在这种情况下,很明显,逻辑很大程度上取决于哪个应用程序被认为是当前应用程序。在上面显示的前端/后端示例中,前端和后端应用程序都可以具有/ login路由,所以 url_for('/ login')应该返回不同的东西,这取决于视图是处理前端还是后端应用程序的请求。



要回答你的问题...




在请求或
应用程序上下文中,stack的目的是什么?

从请求上下文文档:


由于请求上下文是在内部维护的, b $ b可以多次推送和弹出。这是非常方便的实施
的东西,如内部重定向。

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



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


这两个单独的堆栈,还是它们都是一个堆栈的一部分?

他们是两单独的堆栈。但是,这是一个实现细节。更重要的不是有一个堆栈,而是在任何时候你可以得到当前的应用程序或请求(堆栈顶部)。

< blockquote>

请求上下文是否被压入堆栈,或者它本身是堆栈?

请求上下文是请求上下文堆栈的一个项目。类似于应用上下文和应用上下文栈。


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

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



然而,在某些情况下,您可能想自己操作堆栈。



在请求之外运行代码



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

<$ p $ b $ app = Flask(__ name__)
db = SQLAlchemy()#初始化Flask-SQLAlchemy扩展对象
db.init_app(app)
应用程序
db



> c $ c>值应该从shell运行的脚本中。例如,一个setup_tables.py脚本...

  from myapp导入应用程序,db 

#设置模型
db.create_all()

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



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



解决方案是自己推送应用程序上下文,这可以通过执行...

  from myapp导入应用程序,db 

#使用app.app_context()设置模型

db.create_all()

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



测试

另一个你想要操纵栈的情况是测试。您可以创建一个处理请求的单元测试,然后检查结果:

  import unittest $ b $ from flask import request 
$ b $ class MyTest(unittest.TestCase):
def test_thing(self):
with app.test_request_context('/?next=http://example.com/')作为ctx:
#您现在可以使用`request`在请求上下文堆栈中查看属性。

#现在请求上下文栈是空的


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.

解决方案

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.

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
})

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.

Contexts

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

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.

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.

Putting it Together

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).

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

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.

To answer your questions...

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

From the Request Context docs:

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.

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.

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?

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.

Running code outside of a request

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)

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()

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.

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()

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

Testing

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天全站免登陆