Flask1.0.2系列(十五) 使用蓝图将应用程序模块化

英文原文地址:http://flask.pocoo.org/docs/1.0/blueprints/

若有翻译错误或者不尽人意之处,请指出,谢谢~


        (新增于0.7版本。)

        Flask使用一个蓝图(blueprints)的概念来标记应用程序组件,并且在一个应用程序之中或者跨越多个应用程序之间都支持常用的模式。蓝图能很好地简化大型应用程序的工作方式,并且提供一个中心方法来为Flask扩展进行注册到这个应用程序的操作。Blueprint对象的工作方式与Flask应用程序对象相似,但是它实际上并不是一个应用程序。相反,它是一个如何构建或者扩展一个应用程序的蓝图(blueprint)。


1. 为什么要使用蓝图?

        在Flask中,蓝图专注于下面这些事:

        ● 将一个应用程序分解为一组蓝图。对于大型应用程序来说这是很理想的状态;一个项目可以实例化一个应用程序对象,实例化一系列扩展,并且注册一个蓝图集合。

        ● 在一个URL前缀和(或)子域上为应用程序注册一个蓝图。在URL前缀/子域名中的参数成为了蓝图中所有视图方法的常见视图参数(带有默认值)。

        ● 在具有不同URL规则的应用程序上可以多次注册一个蓝图。

        ● 通过蓝图可以提供模板过滤器,静态文件,模板,以及其他工具。蓝图不必实现应用程序或者视图方法。

扫描二维码关注公众号,回复: 464105 查看本文章

        ● 在初始化一个Flask扩展时,能够在这些情况下注册一个应用程序的蓝图。

        Flask中的蓝图并不是一个可插拔的应用,因为蓝图实际上不是一个应用程序——它是一个可以在应用程序上注册的操作集合,甚至可以多次注册。为什么没有多个应用程序对象呢?你可以这样做(后面讲解应用程序调度时会讲到),但是你的应用程序应该抽离配置并且将在WSGI层进行管理。

        实际上,蓝图是在Flask层提供分离,共享应用程序配置,并且在必要的时候,能在注册时改变一个应用程序对象。缺点是,一旦应用程序被创建了,你就不能注销蓝图,这样就不能破坏整个应用程序对象。


2. 蓝图的概念

        蓝图的基本概念是它们记录了在应用程序上注册时执行的操作。当调度请求和从一个端点到另一个端点生成URL时,Flask将视图方法和蓝图联系起来。


3. 我的第一个蓝图

        这是一个非常基础的蓝图的使用。在这里我们希望实现一个简单渲染静态模板的蓝图:

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)

        当你使用@simple_page.route装饰器绑定一个方法,蓝图将会记录在稍后注册的应用程序上,注册函数show的意图。此外,它还会在方法对应的端点前面加上,使用Blueprint构造器生成的蓝图对象的名称(在这里是simple_page)。


4. 注册蓝图

        你要如何注册蓝图呢?就像这样:

from flask import Flask
from yourapplication.simple_page import simple_page


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

        如果你检查在应用程序上注册的规则,你会发现这些内容:

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

        第一条显然是应用程序自身的静态文件。其他两条是simple_page蓝图的show方法。正如你所看到的,它们的前缀是蓝图的名称,并且由一个‘.’分隔开。

        然而,蓝图也能被安装到不同的地方:

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

        并且可以确定,它们将生成如下规则:

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

        最重要的是,你可以多次注册蓝图,虽然不是每个蓝图都可能正确响应。实际上,它依赖于蓝图是何如实现的,如果它会被注册多次的话。


5. 蓝图资源

        蓝图也能提供资源。有时候,你可能希望只引入一个蓝图提供的资源。

5.1 蓝图资源文件夹

        就像一般的应用程序那样,蓝图也是被认为包含在一个文件夹中的。虽然多个蓝图可以放在同一个文件夹中,但最好不要这样做,并且这也是不推荐的。

        文件夹名称通常是由Blueprint第二个参数推导出来的,这个参数通常使用__name__。这个参数指定了,本地Python模块或者包对应到这个蓝图。如果它指向一个实际的Python包,那么这个包(在文件系统上的一个文件夹)就是资源文件夹。如果它指向一个模块,这个模块包含的包就是资源文件夹。你可以访问Blueprint.root_path属性来查看资源文件夹:

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

        为了从这个文件夹快速打开资源,你可以使用open_resource()方法:

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

5.2 静态文件

        蓝图在创建的时候可以通过static_folder参数,来公开带有静态文件的文件夹。这个文件夹既可以是绝对路径也可以是相对路径:

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

        默认情况下,路径的最右边部分在web上是公开的。这可以使用static_url_path参数来改变。因为这个文件夹在这里被叫做static,它将在蓝图的url_prefix+/static中可用。如果蓝图有前缀/admin,静态URL将变成/admin/static。

        这个端点被命名为blueprint_name.static。你可以使用url_for()来生成它的URL,这样你就可以使用应用程序的静态文件夹了。

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

        然而,如果蓝图没有url_prefix,那么就不能访问蓝图的静态文件夹了。因为在这种情况下,它的URL将变成/static,而应用程序的/static路由拥有更高的优先级。与模板文件夹不同的是,如果在应用程序的静态文件夹中不存在指定的文件,那么蓝图的静态文件夹将不会被搜索。

5.3 模板

        如果你想要蓝图公开模板,你可以在Blueprint构造器中提供template_folder参数:

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

        对于静态文件夹来说,这个路径可以使绝对或者相对路径来指向蓝图的资源文件夹。

        蓝图的模板文件夹被添加到了模板的搜索路径中,但是它的优先级低于实际应用程序的模板文件夹。通过这种方式,你可以轻松覆盖蓝图在实际应用程序中提供的模板。这也意味着,如果你不希望一个蓝图模板被意外覆盖,请确保没有其他蓝图或者实际应用程序模板拥有同样的相对路径。当多个蓝图提供同样的相对模板路径时,第一个蓝图的注册将优先于其他蓝图。

        因此如果你有一个蓝图在文件夹yourapplication/admin中,你希望渲染指定模板‘admin/index.html’,并且你已经提供templates作为一个template_folder,你必须创建一个像这样的文件:yourapplication/admin/templates/admin/index.html。因为路径中的admin是用来将指定我们获取到蓝图模板文件夹下叫做‘index.html’的模板,而不是获取到实际应用程序模板文件夹中叫做‘index.html’的模板。

        重申一次:如果你有一个叫做admin的蓝图,并且你希望渲染这个蓝图中特定的一个叫做‘index.html’的模板,最好的方法是将你的模板布置成这样:

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

        然后,当你想要渲染这个模板,使用admin/inidex.html作为名称来查找这个模板。如果你遭遇到加载了一个不正确的模板的情况,开启EXPLAIN_TEMPLATE_LOADING配置变量,这样可以在render_template调用的时候打印出模板的位置。


6. 构建URL

        如果你想要从一个页面链接到另一个页面,你可以使用url_for()方法,只不过在URL端点的前面需要加上蓝图名称和一个点(.):

url_for('admin.index')

        此外,如果你在蓝图的一个视图方法中,或者一个渲染的模板中,并且你希望链接到其他同样属于这个蓝图的端点,你可以只使用一个点(.):

url_for('.index')

        这样将链接到admin.index。


7. 错误处理程序

        就像Flask应用程序对象那样,蓝图也支持errorhandler装饰器。因此,它能很方便地创建蓝图特定的错误页面。

        这里有一个“404 Page Not Found”异常的例子:

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

        大多数errorhandler能够按照预期工作;然而,这里有一个涉及到404和405异常处理程序的警告。这些errorhandler仅仅在适当的raise语句中,或者在其他蓝图的视图方法中调用abort中,才会被注入(即调用)。它们不会在一个非法的URL访问中被注入。这是因为蓝图没有“拥有”一个确定的URL空间,因此应用程序实例在得到一个非法的URL时,没有办法知道蓝图的哪一个errorhandler应该被运行。如果你想要针对这些基于URL前缀的错误,执行不同的处理对策,它们可以在应用程序级别使用request代理对象来定义:

@app.errorhandler(404)
@app.errorhandler(405)
def _handle_api_error(ex):
    if request.path.startswith('/api/'):
        return jsonify_error(ex)
    else:
        return ex

        更多错误处理,在自定义错误页面中有所讲解。

猜你喜欢

转载自blog.csdn.net/regandu/article/details/80262452