Python 烧瓶的用途是什么;什么是上下文堆栈?

Python 烧瓶的用途是什么;什么是上下文堆栈?,python,flask,Python,Flask,我使用请求/应用程序上下文已经有一段时间了,但我没有完全理解它是如何工作的,也没有完全理解它为什么要这样设计。当涉及到请求或应用程序上下文时,“堆栈”的用途是什么?这两个堆栈是分开的,还是都是一个堆栈的一部分?是将请求上下文推送到堆栈上,还是堆栈本身?我是否能够将多个上下文推/弹出到彼此的顶部?如果是,我为什么要这样做 对于所有的问题,我很抱歉,但在阅读了有关请求上下文和应用程序上下文的文档后,我仍然感到困惑。多个应用程序 应用程序上下文(及其用途)确实令人困惑,直到您意识到Flask可以有多个

我使用请求/应用程序上下文已经有一段时间了,但我没有完全理解它是如何工作的,也没有完全理解它为什么要这样设计。当涉及到请求或应用程序上下文时,“堆栈”的用途是什么?这两个堆栈是分开的,还是都是一个堆栈的一部分?是将请求上下文推送到堆栈上,还是堆栈本身?我是否能够将多个上下文推/弹出到彼此的顶部?如果是,我为什么要这样做

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

多个应用程序 应用程序上下文(及其用途)确实令人困惑,直到您意识到Flask可以有多个应用程序。想象一下,您希望一个WSGI Python解释器运行多个Flask应用程序的情况。我们这里说的不是蓝图,而是完全不同的烧瓶应用

您可以将其设置为类似于以下示例:

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

上下文 使用Flask时,通常会使用全局变量访问各种功能。例如,您可能有读取

from flask import request
然后,在查看期间,您可以使用
request
访问当前请求的信息。显然,
request
不是一个正常的全局变量;实际上,它是一种价值。换句话说,在幕后有一种神奇的说法:“当我调用
request.path
时,从当前请求的
request
对象中获取
path
属性。”两个不同的请求将对
request.path
产生不同的结果

事实上,即使使用多个线程运行Flask,Flask也足够智能,可以隔离请求对象。这样,两个线程(每个线程处理一个不同的请求)就可以同时调用
request.path
,并获得各自请求的正确信息

# this would happen some time near the end of request processing
release(local) # aka local.__release_local__()
把它放在一起 因此,我们已经看到Flask可以在同一个解释器中处理多个应用程序,而且由于Flask允许您使用“上下文本地”全局变量,因此必须有某种机制来确定“当前”请求是什么(以便执行诸如
request.path
)之类的操作)

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

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

from flask import url_for
与我们的
请求
示例一样,函数的
url\u具有依赖于当前环境的逻辑。然而,在这种情况下,很明显,逻辑在很大程度上取决于哪个应用程序被视为“当前”应用程序。在上面显示的前端/后端示例中,“前端”和“后端”应用程序都可能有一个“/登录”路由,因此('/login')
url_应该返回不同的内容,这取决于视图是处理前端还是后端应用程序的请求

要回答你的问题。。。 当涉及到请求或请求时,“堆栈”的用途是什么 应用程序上下文

从请求上下文文档:

因为请求上下文是作为堆栈在内部维护的 可以多次推动和弹出。这非常便于实现 比如内部重定向

换句话说,即使在这些“当前”请求或“当前”应用程序堆栈上通常会有0或1项,也可能会有更多项

给出的示例是让请求返回“内部重定向”的结果。假设一个用户请求a,但您希望返回到用户B。在大多数情况下,您向用户发出重定向,并将用户指向资源B,这意味着用户将运行第二个请求以获取B。处理此问题的一种稍微不同的方法是执行内部重定向,这意味着在处理a时,Flask将向自己发出一个新的资源B请求,并将第二个请求的结果用作用户原始请求的结果

这两个堆栈是分开的,还是都是一个堆栈的一部分

是的。然而,这是一个实现细节。更重要的不是有一个堆栈,而是在任何时候都可以获得“当前”应用程序或请求(堆栈顶部)

是将请求上下文推送到堆栈上,还是堆栈本身

“请求上下文”是“请求上下文堆栈”的一项。与“应用程序上下文”和“应用程序上下文堆栈”类似

我是否能够将多个上下文推/弹出到彼此的顶部?如果是,, 我为什么要这么做

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

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

在请求之外运行代码 人们面临的一个典型问题是,他们使用Flask SQLAlchemy扩展来设置SQL数据库和模型定义,代码如下所示

app = Flask(__name__)
db = SQLAlchemy() # Initialize the Flask-SQLAlchemy extension object
db.init_app(app)
然后他们在脚本中使用
app
db
值,该脚本应该从shell运行。例如,“setup_tables.py”脚本

from myapp import app, db

# Set up models
db.create_all()
在这种情况下,Flask SQLAlchemy扩展知道
app
应用程序,但在
create_all()
期间,它会抛出一个错误,抱怨没有应用程序
from myapp import app, db

# Set up models
with app.app_context():
    db.create_all()
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
try:
    from greenlet import get_ident
except ImportError:
    from thread import get_ident
# globally
local = Local()

# ...

# on thread 1
local.first_name = 'John'

# ...

# on thread 2
local.first_name = 'Debbie'
class Local(object)
    def __init__(self):
        self.storage = {}

    def __getattr__(self, name):
        context_id = get_ident() # we get the current thread's or greenlet's id
        contextual_storage = self.storage.setdefault(context_id, {})
        try:
            return contextual_storage[name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        context_id = get_ident()
        contextual_storage = self.storage.setdefault(context_id, {})
        contextual_storage[name] = value

    def __release_local__(self):
        context_id = get_ident()
        self.storage.pop(context_id, None)

local = Local()
class LocalProxy(object):
    def __init__(self, local, name):
        # `local` here is either an actual `Local` object, that can be used
        # to find the object of interest, here identified by `name`, or it's
        # a callable that can resolve to that proxied object
        self.local = local
        # `name` is an identifier that will be passed to the local to find the
        # object of interest.
        self.name = name

    def _get_current_object(self):
        # if `self.local` is truly a `Local` it means that it implements
        # the `__release_local__()` method which, as its name implies, is
        # normally used to release the local. We simply look for it here
        # to identify which is actually a Local and which is rather just
        # a callable:
        if hasattr(self.local, '__release_local__'):
            try:
                return getattr(self.local, self.name)
            except AttributeError:
                raise RuntimeError('no object bound to %s' % self.name)

        # if self.local is not actually a Local it must be a callable that 
        # would resolve to the object of interest.
        return self.local(self.name)

    # Now for the LocalProxy to perform its intended duties i.e. proxying 
    # to an underlying object located somewhere in a Local, we turn all magic
    # methods into proxies for the same methods in the object of interest.
    @property
    def __dict__(self):
        try:
            return self._get_current_object().__dict__
        except RuntimeError:
            raise AttributeError('__dict__')

    def __repr__(self):
        try:
            return repr(self._get_current_object())
        except RuntimeError:
            return '<%s unbound>' % self.__class__.__name__

    def __bool__(self):
        try:
            return bool(self._get_current_object())
        except RuntimeError:
            return False

    # ... etc etc ... 

    def __getattr__(self, name):
        if name == '__members__':
            return dir(self._get_current_object())
        return getattr(self._get_current_object(), name)

    def __setitem__(self, key, value):
        self._get_current_object()[key] = value

    def __delitem__(self, key):
        del self._get_current_object()[key]

    # ... and so on ...

    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
    __delattr__ = lambda x, n: delattr(x._get_current_object(), n)
    __str__ = lambda x: str(x._get_current_object())
    __lt__ = lambda x, o: x._get_current_object() < o
    __le__ = lambda x, o: x._get_current_object() <= o
    __eq__ = lambda x, o: x._get_current_object() == o

    # ... and so forth ...
# this would happen some time near application start-up
local = Local()
request = LocalProxy(local, 'request')
g = LocalProxy(local, 'g')
# this would happen early during processing of an http request
local.request = RequestContext(http_environment)
local.g = SomeGeneralPurposeContainer()
# this would happen some time near the end of request processing
release(local) # aka local.__release_local__()
class Local(object):
    # ... 
    # ... all same stuff as before go here ...
    # ... 

    def __call__(self, name):
        return LocalProxy(self, name)

# now you can do
local = Local()
request = local('request')
g = local('g')
class LocalStack(object):

    def __init__(self):
        self.local = Local()

    def push(self, obj):
        """Pushes a new item to the stack"""
        rv = getattr(self.local, 'stack', None)
        if rv is None:
            self.local.stack = rv = []
        rv.append(obj)
        return rv

    def pop(self):
        """Removes the topmost item from the stack, will return the
        old value or `None` if the stack was already empty.
        """
        stack = getattr(self.local, 'stack', None)
        if stack is None:
            return None
        elif len(stack) == 1:
            release_local(self.local) # this simply releases the local
            return stack[-1]
        else:
            return stack.pop()

    @property
    def top(self):
        """The topmost item on the stack.  If the stack is empty,
        `None` is returned.
        """
        try:
            return self.local.stack[-1]
        except (AttributeError, IndexError):
            return None
_request_ctx_stack = LocalStack()
def _find_request():
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of request context')
    return top.request
request = LocalProxy(_find_request)

def _find_session():
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of request context')
    return top.session
session = LocalProxy(_find_session)

_app_ctx_stack = LocalStack()
def _find_g():
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of application context')
    return top.g
g = LocalProxy(_find_g)

def _find_app():
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of application context')
    return top.app
current_app = LocalProxy(_find_app)
app = Flask(*config, **kwconfig)

# ...
app(environ, start_response) # aka app.__call__(environ, start_response)
def Flask(object):

    # ...

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)

    def wsgi_app(self, environ, start_response):
        ctx = RequestContext(self, environ)
        ctx.push()
        try:
            # process the request here
            # raise error if any
            # return Response
        finally:
            ctx.pop()

    # ...
class RequestContext(object):

    def __init__(self, app, environ, request=None):
        self.app = app
        if request is None:
            request = app.request_class(environ)
        self.request = request
        self.url_adapter = app.create_url_adapter(self.request)
        self.session = self.app.open_session(self.request)
        if self.session is None:
            self.session = self.app.make_null_session()
        self.flashes = None

    def push(self):
        _request_ctx_stack.push(self)

    def pop(self):
        _request_ctx_stack.pop()
class User(object):
    def __init__(self):
        self.userid = None
def get_user(_local):
    try:
        # get user object in current thread or greenlet
        return _local.user
    except AttributeError:
        # if user object is not set in current thread ,set empty user object 
       _local.user = User()
    return _local.user
usercontext = LocalProxy(partial(get_user, Local()))