应用工厂¶
如果你已经在应用中使用了包和蓝图(使用蓝图模块化应用),那么还有一些很不错的方法可以进一步改进使用体验。一个常见的模式是在蓝图被导入时创建应用对象。但如果你将该对象的创建过程移动到一个函数中,那么稍后你就可以创建该应用的多个实例。
So why would you want to do this?
Testing. You can have instances of the application with different settings to test every case.
Multiple instances. Imagine you want to run different versions of the same application. Of course you could have multiple instances with different configs set up in your webserver, but if you use factories, you can have multiple instances of the same application running in the same application process which can be handy.
So how would you then actually implement that?
Basic Factories¶
The idea is to set up the application in a function. Like this:
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. How do you
get access to the application with the config? 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.
Factories & Extensions¶
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 扩展开发.
Using Applications¶
To run such an application, you can use the flask command:
$ flask --app hello run
Flask will automatically detect the factory if it is named
create_app or make_app in hello. You can also pass arguments
to the factory like this:
$ flask --app hello:create_app(local_auth=True) run
Then the create_app factory in myapp is called with the keyword
argument local_auth=True. See 命令行接口 for more detail.
Factory Improvements¶
The factory function above is not very clever, but you can improve it. The following changes are straightforward to implement:
Make it possible to pass in configuration values for unit tests so that you don't have to create config files on the filesystem.
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.)
Add in WSGI middlewares when the application is being created if necessary.