203

在 Flask 0.10 中,我看到它将从请求上下文转移到应用程序上下文,这让我对.gg

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

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

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

4

2 回答 2

135

由 Markus 链接的Advanced Flask Patterns解释了g0.10中的一些更改:

  • g现在生活在应用程序上下文中。
  • 每个请求都会推送一个新的应用程序上下文,擦除旧的应用程序上下文,因此g仍可用于为每个请求设置标志,而无需更改代码。
  • 调用后 会弹出应用程序上下文teardown_request。(Armin 的演示文稿解释说这是因为创建数据库连接之类的任务是为请求设置环境的任务,不应在内部处理before_requestand after_request
于 2013-02-26T23:06:46.323 回答
113

作为此线程中信息的附录:我也对它的行为感到有些困惑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

As theY4Kman said above, "Every request pushes a new application context". And as the Flask docs say, the application context "will not be shared between requests". Now, what hasn't been explicitly stated (although I guess it's implied from these statements), and what my testing clearly shows, is that you should never explicitly create multiple request contexts nested inside one application context, because flask.g (and co) doesn't have any magic whereby it functions in the two different "levels" of context, with different states existing independently at the application and request levels.

The reality is that "application context" is potentially quite a misleading name, because app.app_context() is a per-request context, exactly the same as the "request context". Think of it as a "request context lite", only required in the case where you need some of the variables that normally require a request context, but you don't need access to any request object (e.g. when running batch DB operations in a shell script). If you try and extend the application context to encompass more than one request context, you're asking for trouble. So, rather than my test above, you should instead write code like this with Flask's contexts:

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

Which will give the expected results:

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
于 2015-10-28T04:17:24.827 回答