应用程序的工厂函数

If you are already using packages and blueprints for your application (Modular Applications with Blueprints) there are a couple of really nice ways to further improve the experience. A common pattern is creating the application object when the blueprint is imported. But if you move the creation of this object, into a function, you can then create multiple instances of this app later.

所以,你为什么要这么做?

  1. Testing. You can have instances of the application with different settings to test every case.
  2. Multiple instances. 想象一下你需要运行同一个应用的不同版本。你当然可以在你的Web服务器中配置多个实例并分配不同的配置,但是如果你使用工厂函数, 你就可以在一个随手即得的进程中运行这一个应用的不同实例了!

So how would you then actually implement that?

基础的工厂函数

这个想法是在一个函数中建立应用。像这样:

def create_app(config_filename):
    app = Flask(__name__)
    app.config.from_pyfile(config_filename)

    from yourapplication.model import db
    db.init_app(app)

    from yourapplication.views.admin import admin
    from yourapplication.views.frontend import frontend
    app.register_blueprint(admin)
    app.register_blueprint(frontend)

    return app

The downside is that you cannot use the application object in the blueprints at import time. You can however use it from within a request. 如果获取当前配置下的对应的应用程序对象呢?Use current_app:

from flask import current_app, Blueprint, render_template
admin = Blueprint('admin', __name__, url_prefix='/admin')

@admin.route('/')
def index():
    return render_template(current_app.config['INDEX_TEMPLATE'])

Here we look up the name of a template in the config.

工厂函数和扩展

It’s preferable to create your extensions and app factories so that the extension object does not initially get bound to the application.

Using Flask-SQLAlchemy, as an example, you should not do something along those lines:

def create_app(config_filename):
    app = Flask(__name__)
    app.config.from_pyfile(config_filename)

    db = SQLAlchemy(app)

But, rather, in model.py (or equivalent):

db = SQLAlchemy()

and in your application.py (or equivalent):

def create_app(config_filename):
    app = Flask(__name__)
    app.config.from_pyfile(config_filename)

    from yourapplication.model import db
    db.init_app(app)

Using this design pattern, no application-specific state is stored on the extension object, so one extension object can be used for multiple apps. For more information about the design of extensions refer to Flask Extension Development.

使用应用

所以,要使用这样的一个应用,你必须先在一个单独的文件中创建这个应用,否则flask命令将找不到它。下面是一个示例exampleapp.py,它创建这样的应用︰

from yourapplication import create_app
app = create_app('/path/to/config.cfg')

It can then be used with the flask command:

export FLASK_APP=exampleapp
flask run

工厂函数的改进

前文所提供的工厂函数并不是特别聪明好用,你可以改进它。如下的改变很直接而且可行:

  1. make it possible to pass in configuration values for unittests so that you don’t have to create config files on the filesystem
  2. call a function from a blueprint when the application is setting up so that you have a place to modify attributes of the application (like hooking in before / after request handlers etc.)
  3. Add in WSGI middlewares when the application is creating if necessary.