用蓝图实现模块化的应用

New in version 0.7.

Flask用蓝图的概念来在一个应用中或跨应用制作应用组件和支持通用的模式。Blueprints can greatly simplify how large applications work and provide a central means for Flask extensions to register operations on applications. 一个Blueprint对象与Flask应用对象的工作方式很像,但它确实不是一个应用。相反,它是一个描述如何构建或扩展应用的蓝图

为什么使用蓝图?

Blueprints in Flask are intended for these cases:

  • Factor an application into a set of blueprints. 这对大型应用是理想的;一个项目可以实例化 一个应用对象,初始化几个扩展,并注册一集合的蓝图。
  • Register a blueprint on an application at a URL prefix and/or subdomain. Parameters in the URL prefix/subdomain become common view arguments (with defaults) across all view functions in the blueprint.
  • Register a blueprint multiple times on an application with different URL rules.
  • Provide template filters, static files, templates, and other utilities through blueprints. A blueprint does not have to implement applications or view functions.
  • Register a blueprint on an application for any of these cases when initializing a Flask extension.

A blueprint in Flask is not a pluggable app because it is not actually an application – it’s a set of operations which can be registered on an application, even multiple times. Why not have multiple application objects? You can do that (see Application Dispatching), but your applications will have separate configs and will be managed at the WSGI layer.

Blueprints instead provide separation at the Flask level, share application config, and can change an application object as necessary with being registered. 它的缺点是你不能在应用创建后撤销注册一个蓝图而不销毁整个应用对象。

蓝图的概念

The basic concept of blueprints is that they record operations to execute when registered on an application. Flask associates view functions with blueprints when dispatching requests and generating URLs from one endpoint to another.

我的第一个蓝图

This is what a very basic blueprint looks like. In this case we want to implement a blueprint that does simple rendering of static templates:

from flask import Blueprint, render_template, abort
from jinja2 import TemplateNotFound

simple_page = Blueprint('simple_page', __name__,
                        template_folder='templates')

@simple_page.route('/', defaults={'page': 'index'})
@simple_page.route('/<page>')
def show(page):
    try:
        return render_template('pages/%s.html' % page)
    except TemplateNotFound:
        abort(404)

When you bind a function with the help of the @simple_page.route decorator the blueprint will record the intention of registering the function show on the application when it’s later registered. 此外,它会给函数的端点加上由Blueprint 的构造函数中给出的蓝图的名称作为前缀(在此例中是simple_page)。

注册蓝图

So how do you register that blueprint? 像这样:

from flask import Flask
from yourapplication.simple_page import simple_page

app = Flask(__name__)
app.register_blueprint(simple_page)

If you check the rules registered on the application, you will find these:

[<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
 <Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
 <Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>]

The first one is obviously from the application itself for the static files. The other two are for the show function of the simple_page blueprint. As you can see, they are also prefixed with the name of the blueprint and separated by a dot (.).

Blueprints however can also be mounted at different locations:

app.register_blueprint(simple_page, url_prefix='/pages')

And sure enough, these are the generated rules:

[<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
 <Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
 <Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>]

On top of that you can register blueprints multiple times though not every blueprint might respond properly to that. In fact it depends on how the blueprint is implemented if it can be mounted more than once.

蓝图资源

Blueprints can provide resources as well. Sometimes you might want to introduce a blueprint only for the resources it provides.

蓝图资源文件夹

Like for regular applications, blueprints are considered to be contained in a folder. While multiple blueprints can originate from the same folder, it does not have to be the case and it’s usually not recommended.

这个文件夹会从Blueprint的第二个参数中推断出来,通常是__name__This argument specifies what logical Python module or package corresponds to the blueprint. If it points to an actual Python package that package (which is a folder on the filesystem) is the resource folder. If it’s a module, the package the module is contained in will be the resource folder. You can access the Blueprint.root_path property to see what the resource folder is:

>>> simple_page.root_path
'/Users/username/TestProject/yourapplication'

可以使用open_resource()函数来快速从这个文件夹打开源文件:

with simple_page.open_resource('static/style.css') as f:
    code = f.read()

静态文件

A blueprint can expose a folder with static files by providing a path to a folder on the filesystem via the static_folder keyword argument. It can either be an absolute path or one relative to the folder of the blueprint:

admin = Blueprint('admin', __name__, static_folder='static')

By default the rightmost part of the path is where it is exposed on the web. 因为这里这个文件夹叫做static,它会在蓝图 + /static的位置上可用。Say the blueprint is registered for /admin the static folder will be at /admin/static.

The endpoint is named blueprint_name.static so you can generate URLs to it like you would do to the static folder of the application:

url_for('admin.static', filename='style.css')

模板

如果你想要蓝图暴露模板,你可以提供Blueprint构造函数中的template_folder参数来实现:

admin = Blueprint('admin', __name__, template_folder='templates')

For static files, the path can be absolute or relative to the blueprint resource folder.

The template folder is added to the search path of templates but with a lower priority than the actual application’s template folder. That way you can easily override templates that a blueprint provides in the actual application. This also means that if you don’t want a blueprint template to be accidentally overridden, make sure that no other blueprint or actual application template has the same relative path. When multiple blueprints provide the same relative template path the first blueprint registered takes precedence over the others.

因此,如果您在文件夹yourapplication/admin中有一个蓝图,并且想渲染模板'admin/index.html',同时已经提供将templates 作为 template_folder ,那么您将需要创建一个如下的文件:yourapplication/admin/templates/admin/index.html。 The reason for the extra admin folder is to avoid getting our template overridden by a template named index.html in the actual application template folder.

要进一步重申这一点:如果您有一个名为admin的蓝图,并且要渲染一个名为index.html的模板,该模板特定于此蓝图,最好的想法是像这样放置您的模板:

yourpackage/
    blueprints/
        admin/
            templates/
                admin/
                    index.html
            __init__.py

And then when you want to render the template, use admin/index.html as the name to look up the template by. If you encounter problems loading the correct templates enable the EXPLAIN_TEMPLATE_LOADING config variable which will instruct Flask to print out the steps it goes through to locate templates on every render_template call.

构造 URL

当你想要从一个页面链接到另一个页面,你可以像通常一个样使用url_for()函数,只是你要在URL的末端前缀上蓝图的名称和一个点(.):

url_for('admin.index')

Additionally if you are in a view function of a blueprint or a rendered template and you want to link to another endpoint of the same blueprint, you can use relative redirects by prefixing the endpoint with a dot only:

url_for('.index')

This will link to admin.index for instance in case the current request was dispatched to any other admin blueprint endpoint.

错误处理

Blueprint支持和Flask应用对象一样的错误处理装饰器,所以很容易制作Blueprint定自定义的错误页。

这里是一个"404 Page Not Found"异常的例子︰

@simple_page.errorhandler(404)
def page_not_found(e):
    return render_template('pages/404.html')

有关错误处理的详细信息请参阅自定义错误页面