应用上下文

应用上下文在请求、命令行命令以及其他活动期间保持对应用层面数据的追踪。相比起将应用上下文传递到每个函数当中,Flask 使用了 current_appg 这两个代理对象来访问应用上下文。

This is similar to 请求上下文, which keeps track of request-level data during a request. A corresponding application context is pushed when a request context is pushed.

上下文的目的

Flask 应用对象所拥有的属性(attributes),如 config,在视图函数与 CLI 命令 中会用到。然而,在项目的模块里导入(import) app 可能会带来循环导入的问题。当使用 应用工厂模式 、编写可重用的 蓝图 或者 扩展app 实例将完全无法被导入。

Flask 使用 应用上下文 来解决这个问题。在这种情况下,使用指向当前活动的应用对象的代理 current_app ,而不是直接引用 app

Flask 在处理请求时候会自动 推入 一个应用上下文。视图函数、错误处理器、还有其他在请求中运行的函数将可以访问 current_app

运行使用 @app.cli.command() 注册到 Flask.cli 的命令行命令,Flask 也会自动推入应用上下文。

上下文的生命周期

应用上下文会在必要的时候创建以及销毁。当 Flask 应用准备处理一个请求时,将推入一个应用上下文以及 请求上下文。当请求结束的时候,弹出应用上下文以及请求上下文。通常来说,一个应用上下文与请求上下文拥有一致的生命周期。

关于上下文的工作原理以及请求的完整生命周期的更多信息,参见 请求上下文

手动推入一个上下文

若在应用上下文之外的地方尝试访问 current_app ,或在任何使用此对象的情况下,会收到这样的错误信息:

RuntimeError: Working outside of application context.

This typically means that you attempted to use functionality that
needed to interface with the current application object in some way.
To solve this, set up an application context with app.app_context().

如果在配置应用的时候(如初始化扩展)看到这样的错误信息,可以手动推入一个上下文以访问 app。在 with 语句中使用 app_context() 上下文管理器对象,所有在块内的运行的代码将可以访问 current_app

def create_app():
    app = Flask(__name__)

    with app.app_context():
        init_db()

    return app

如果你在配置应用以外场景遇到这个错误,绝大多数情况下意味着这些代码应该转移到视图函数以及命令行命令下。

存储数据

在请求中或者命令行命令里,应用上下文是存储通用数据的好地方。Flask 为此提供了 g 对象 。这是一个简单的命名空间对象,与应用上下文有同样的生命周期。

备注

g 表示 “global”(全局)的意思,这也意味着数据 在上下文中 是属于全局的。在上下文结束的时候, g 对象中的数据会丢失,因此这不是请求之间存放数据的好地方。这时候可以使用 session 或者数据库来存放跨请求的数据。

一种使用 g 的常见场景是管理一个请求中的资源。

  1. get_X() 在资源 X 不存在的时候创建了资源 X,将其缓存到 g.X

  2. teardown_X() 在资源存在的时候关闭或者释放它。函数被注册为 teardown_appcontext() 钩子函数。

例如,可以使用以下方式来处理数据库连接:

from flask import g

def get_db():
    if 'db' not in g:
        g.db = connect_to_database()

    return g.db

@app.teardown_appcontext
def teardown_db(exception):
    db = g.pop('db', None)

    if db is not None:
        db.close()

在请求当中,所有调用 get_db() 的地方均会返回一样的连接,而这个连接会在请求结束的时候自动关闭。

通过 LocalProxy 可以从 get_db() 创建一个新的上下文本地变量:

from werkzeug.local import LocalProxy
db = LocalProxy(get_db)

访问 db 会内部调用 get_db,就像访问 current_app 的调用机制一样。


在编写扩展的过程当中,g 应该为用户代码保留。扩展的内部数据应该存储在上下文对象的属性当中,除此以外还要保证使用一个足够独特的属性名称。当前的上下文可以通过 _app_ctx_stack.top 获取。更多信息参见 Flask Extension Development

事件与信号

在应用上下文被弹出的时候,应用会调用注册在 teardown_appcontext() 的函数。

如果 signals_available 为真,下列信号将被发送:appcontext_pushedappcontext_tearing_down、以及 appcontext_popped.