应用上下文

New in version 0.9.

Flask背后的设计理念之一是,代码执行有两种不同的“状态”。应用建立状态,它是应用在模块级别的隐式状态。它起始于Flask对象实例化的时候,隐式地结束于第一个请求进来。当应用处于这个状态的时候,有下面一些假设为真:

  • the programmer can modify the application object safely.
  • no request handling happened so far
  • 不会有某个神奇的代理变量指向你刚创建的或者正在修改的应用对象的

相反,在请求处理时,存在一些其它的规则:

  • while a request is active, the context local objects (flask.request and others) point to the current request.
  • any code can get hold of these objects at any time.

There is a third state which is sitting in between a little bit. Sometimes you are dealing with an application in a way that is similar to how you interact with applications during request handling; just that there is no request active. Consider, for instance, that you’re sitting in an interactive Python shell and interacting with the application, or a command line application.

The application context is what powers the current_app context local.

应用上下文的目的

应用上下文存在的主要原因是,在过去,请求上下文被附加了一堆函数,但是又没有什么好的解决方案。 因为 Flask 设计的支柱之一是你可以在一个 Python 进程中拥有多个应用。

So how does the code find the “right” application? 在过去,我们推荐显式地到处传递应用,但是这会造成库并不是按照我们想法设计的问题。

A common workaround for that problem was to use the current_app proxy later on, which was bound to the current request’s application reference. Since creating such a request context is an unnecessarily expensive operation in case there is no request around, the application context was introduced.

创建应用上下文

There are two ways to make an application context. The first one is implicit: whenever a request context is pushed, an application context will be created alongside if this is necessary. As a result, you can ignore the existence of the application context unless you need it.

The second way is the explicit way using the app_context() method:

from flask import Flask, current_app

app = Flask(__name__)
with app.app_context():
    # within this block, current_app points to app.
    print current_app.name

The application context is also used by the url_for() function in case a SERVER_NAME was configured. This allows you to generate URLs even in the absence of a request.

If no request context has been pushed and an application context has not been explicitly set, a RuntimeError will be raised.

RuntimeError: Working outside of application context.

上下文的局部性

The application context is created and destroyed as necessary. It never moves between threads and it will not be shared between requests. As such it is the perfect place to store database connection information and other things. The internal stack object is called flask._app_ctx_stack. 扩展可以在最顶层自由地存储额外信息,想象一下它们用一个充分独特的名字在那里存储信息,而不是在flask.g对象里,它是留给用户的代码用的。

更多详情见Flask扩展开发

上下文的用处

上下文的一个典型应用场景就是用来缓存一些我们需要在发生请求之前或者要使用的资源。For instance, database connections are destined to go there. When storing things on the application context unique names should be chosen as this is a place that is shared between Flask applications and extensions.

The most common usage is to split resource management into two parts:

  1. an implicit resource caching on the context.
  2. a context teardown based resource deallocation.

通常来讲,这将会有一个get_X()函数来创建资源X ,如果它还不存在的话,存在的话就直接返回它,另外还会有一个teardown_X()的回调函数用于销毁。

This is an example that connects to a database:

import sqlite3
from flask import g

def get_db():
    db = getattr(g, '_database', None)
    if db is None:
        db = g._database = connect_to_database()
    return db

@app.teardown_appcontext
def teardown_db(exception):
    db = getattr(g, '_database', None)
    if db is not None:
        db.close()

The first time get_db() is called the connection will be established. 可以使用LocalProxy隐式地做到这点:

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

这样的话,用户可以直接访问db,而它在内部调用get_db()