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

223 投票
2 回答
135023 浏览
提问于 2025-04-17 17:09

我在看到,在Flask 0.10版本中,g会从请求上下文转移到应用上下文,这让我对g的使用产生了困惑。

我对Flask 0.9的理解是:

  • g存在于请求上下文中,也就是说,它在请求开始时创建,并且在请求结束之前一直可用。
  • g的目的是作为一个“请求黑板”,我可以在这里放一些与请求相关的内容(比如在请求开始时设置一个标志,然后在结束时处理它,可能通过before_requestafter_request这对函数)。
  • 除了保存请求级别的状态,g还可以并且应该用于资源管理,比如保存数据库连接等。

在Flask 0.10中,以上哪些说法不再成立?有没有人能给我推荐一些资源,讨论一下这次改变的原因?在Flask 0.10中,我应该用什么作为“请求黑板”——我是否应该创建自己的应用/扩展特定的线程局部代理,并在before_request中将其推入上下文栈?如果我的应用程序运行时间很长(不像请求那样短),那么在应用上下文中进行资源管理有什么意义,因为这些资源永远不会被释放呢?

2 个回答

136

关于这个话题,我想补充一些信息:我对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

正如上面提到的,"每个请求都会创建一个新的应用上下文"。而且Flask的文档中也说过,应用上下文"不会在请求之间共享"。现在,有一点没有明确说明(不过我想这些话的意思是暗示了这一点),而我的测试也清楚地表明,你绝对不要在一个应用上下文中显式地创建多个嵌套的请求上下文,因为flask.g(以及其他相关内容)并没有什么魔法,能够在这两种不同的"层级"上下文中独立工作,应用层和请求层的状态是不同的。

实际上,“应用上下文”这个名字可能会让人误解,因为app.app_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
153

高级Flask模式中,正如Markus提到的,解释了在0.10版本中对g的一些变化:

  • g现在是在应用上下文中使用的。
  • 每次请求都会创建一个新的应用上下文,并且会清除旧的上下文,这样g就可以在每次请求中设置标志,而不需要修改代码。
  • 应用上下文会在调用teardown_request之后被移除。(Armin的演示解释说,这样做是因为像创建数据库连接这样的任务是为请求准备环境的工作,应该在before_requestafter_request中处理,而不是在这两个函数里。)

撰写回答