什么时候应该使用 Flask.g?

Posted

技术标签:

【中文标题】什么时候应该使用 Flask.g?【英文标题】:When should Flask.g be used? 【发布时间】:2013-02-11 14:24:00 【问题描述】:

我saw 认为g 将从请求上下文移动到 Flask 0.10 中的应用上下文,这让我对 g 的预期用途感到困惑。

我的理解(对于 Flask 0.9)是:

g 存在于请求上下文中,即在请求开始时重新创建,并且在请求结束之前一直可用 g 旨在用作“请求黑板”,我可以在其中放置与请求持续时间相关的内容(即,在请求的开头设置一个标志并在最后处理它,可能来自 @ 987654327@/after_request对) 除了保持请求级状态,g 可以而且应该用于资源管理,即保持数据库连接等。

哪些句子在 Flask 0.10 中不再正确?有人可以指出我讨论更改的原因的资源吗?我应该在 Flask 0.10 中使用什么作为“请求黑板”——我应该创建自己的应用程序/扩展特定线程本地代理并将其推送到上下文堆栈before_request?如果我的应用程序存在很长时间(不像请求)并且资源永远不会被释放,那么在应用程序上下文中进行资源管理有什么意义?

【问题讨论】:

我同意,这是一个非常奇怪的变化。希望 mitsuhiko 在 0.10 中实现某种请求上下文对象来替换 g,否则听起来很多代码可能会开始开发一些狡猾的错误。 FWIW,Armin Ronacher(Flask 的作者)发布了“高级 Flask 模式”的续集,其中展示了一些关于如何使用新的 flask.g 的示例代码。 speakerdeck.com/mitsuhiko/advanced-flask-patterns-1 一个新的请求上下文也意味着一个新的应用上下文,所以它在正常使用中应该可以正常工作 【参考方案1】:

Advanced Flask Patterns,作为linked by Markus,解释了0.10中对g的一些更改:

g 现在存在于应用程序上下文中。 Every request pushes a new application context,擦除旧的,所以 g 仍然可以用于为每个请求设置标志,而无需更改代码。 在调用teardown_request 之后弹出应用程序上下文。 (Armin 的演示文稿解释说这是因为创建数据库连接之类的任务是设置请求环境的任务,不应在 before_requestafter_request 内处理)

【讨论】:

在你链接的源代码中,当app_ctx is None or app_ctx.app != self.app为False时,旧的应用程序上下文似乎被重用了?这似乎不对,因为应用程序上下文“不会在请求之间共享”... 你指的是pushing of app.app_context()吗?如果是这样,应该注意app_context() 每次调用都会实例化一个新的应用程序上下文——它从不重用上下文。 是的,确实如此,但是当app_ctx is not None and app_ctx.app == self.app 时,app_ctx = self.app.app_context()被执行;在这种情况下只执行self._implicit_app_ctx_stack.append(None) 哦,对不起,我看错了!在生产环境中,每个线程(或 greenlet)只提供一个请求。只推送了一个RequestContext,所以只推送了一个AppContext。但是如果debug mode is on and a request fails, Flask saves the context,那么it can be used with the debugger。 None 附加到_app_ctx_stack 之后,因此当请求被拆除时,它知道暂时不要弹出AppContext。测试客户端也会发生同样的事情,它保留了上下文,因此可以对其进行检查。 所以 g 的范围是每个请求(线程),它不会在后续请求中保留该值。【参考方案2】:

作为此线程中信息的补充:我也对flask.g 的行为感到有些困惑,但一些快速测试帮助我澄清了这一点。这是我尝试过的:

from flask import Flask, g
app = Flask(__name__)

with app.app_context():
    print('in app context, before first request context')
    print('setting g.foo to abc')
    g.foo = 'abc'
    print('g.foo should be abc, is: 0'.format(g.foo))

    with app.test_request_context():
        print('in first request context')
        print('g.foo should be abc, is: 0'.format(g.foo))
        print('setting g.foo to xyz')
        g.foo = 'xyz'
        print('g.foo should be xyz, is: 0'.format(g.foo))

    print('in app context, after first request context')
    print('g.foo should be abc, is: 0'.format(g.foo))

    with app.test_request_context():
        print('in second request context')
        print('g.foo should be abc, is: 0'.format(g.foo))
        print('setting g.foo to pqr')
        g.foo = 'pqr'
        print('g.foo should be pqr, is: 0'.format(g.foo))

    print('in app context, after second request context')
    print('g.foo should be abc, is: 0'.format(g.foo))

这是它给出的输出:

in app context, before first request context
setting g.foo to abc
g.foo should be abc, is: abc  

in first request context
g.foo should be abc, is: abc
setting g.foo to xyz
g.foo should be xyz, is: xyz  

in app context, after first request context
g.foo should be abc, is: xyz  

in second request context
g.foo should be abc, is: xyz
setting g.foo to pqr
g.foo should be pqr, is: pqr  

in app context, after second request context
g.foo should be abc, is: pqr

正如 Y4Kman 上面所说,“每个请求都会推送一个新的应用程序上下文”。而as the Flask docs say,应用程序上下文“不会在请求之间共享”。现在,没有明确说明的内容(尽管我猜这些陈述暗示了这一点),而我的测试清楚地表明,您应该永远明确创建嵌套在一个应用程序上下文中的多个请求上下文,因为flask.g(和 co)没有任何魔力,它可以在两个不同的上下文“级别”中运行,不同的状态独立存在于应用程序和请求级别。

现实情况是,“应用程序上下文”可能是一个非常具有误导性的名称,因为app.app_context()per-request context,与"request context" 完全相同。将其视为“请求上下文精简版”,仅在您需要一些通常需要请求上下文的变量但您不需要访问任何请求对象的情况下才需要(例如,在运行批处理数据库操作时)外壳脚本)。如果您尝试扩展应用程序上下文以包含多个请求上下文,那么您就是在自找麻烦。因此,您应该使用 Flask 的上下文编写这样的代码,而不是我上面的测试:

from flask import Flask, g
app = Flask(__name__)

with app.app_context():
    print('in app context, before first request context')
    print('setting g.foo to abc')
    g.foo = 'abc'
    print('g.foo should be abc, is: 0'.format(g.foo))

with app.test_request_context():
    print('in first request context')
    print('g.foo should be None, is: 0'.format(g.get('foo')))
    print('setting g.foo to xyz')
    g.foo = 'xyz'
    print('g.foo should be xyz, is: 0'.format(g.foo))

with app.test_request_context():
    print('in second request context')
    print('g.foo should be None, is: 0'.format(g.get('foo')))
    print('setting g.foo to pqr')
    g.foo = 'pqr'
    print('g.foo should be pqr, is: 0'.format(g.foo))

这将给出预期的结果:

in app context, before first request context
setting g.foo to abc
g.foo should be abc, is: abc  

in first request context
g.foo should be None, is: None
setting g.foo to xyz
g.foo should be xyz, is: xyz  

in second request context
g.foo should be None, is: None
setting g.foo to pqr
g.foo should be pqr, is: pqr

【讨论】:

由于最后一段而被赞成,flask 的上下文一开始很难理解。从名称中,您会感觉到请求上下文是每个请求的,并且应用程序上下文即使在请求之后仍然存在,或者不受其生命周期的影响。 Mata 是一个很好的解释。谢谢 app_context 视为“WSGI 应用程序上下文”可能会有所帮助,因为 WSGI 应用程序是调用来处理每个请求的可调用对象。所以app_context 不会持续整个 Flask 应用程序的生命周期,而是 WSGI 应用程序的整个生命周期。

以上是关于什么时候应该使用 Flask.g?的主要内容,如果未能解决你的问题,请参考以下文章

Flask - g变量

什么时候应该使用 Docker,什么时候应该使用虚拟机? [复制]

什么时候应该使用 Sql Azure,什么时候应该使用表存储?

什么时候应该使用 Microsoft.Owin 实现,什么时候应该使用 AspNetCore?

什么时候应该使用 JSF 组件,什么时候应该使用 html 标签? [关闭]

我们啥时候应该使用互斥锁,啥时候应该使用信号量