个人认为,引入蓝图,主要将大型的应用,分解成一个个小的部分,这些小的部分就是蓝图。还可以在一个应用的URL前缀、子域上注册一个蓝图;使用不同的URL规则在应用中多次注册蓝图;并且还可以通过蓝图提供模版过滤器、静态文件、模板和其他工具。使用蓝图可以使应用搬到Flask层面中进行管理。
蓝图架构:分为功能式架构和分区式架构。
功能式架构:
按照每部分代码的功能来组织你的应用。所有模版放到同一个文件夹中,静态文件放在另一个文件夹中,而视图放在第三个文件夹中。
yourapp/
__init__.py
static/
templates/
home/
control_panel/
admin/
views/
__init__.py
home.py
control_panel.py
admin.py
models.py
除了yourapp/views/__init__.py,在yourapp/views/文件夹中的每一个.py文件都是一个蓝图。在yourapp/init.py中,我们将加载这些蓝图并在我们的Flask()对象中注册它们。
分区式架构:
在分区式架构中,按照每一部分所属的蓝图来组织你的应用。管理面板的所有的模板,视图和静态文件放在一个文件夹中,用户控制面板则放在另一个文件夹中。
yourapp/
__init__.py
admin/
__init__.py
views.py
static/
templates/
home/
__init__.py
views.py
static/
templates/
control_panel/
__init__.py
views.py
static/
templates/
models.py
在像上面列举的分区式结构,每一个yourapp/之下的文件夹都是一个独立的蓝图。所有的蓝图通过顶级的__init__.py注册到Flask()中。
选择分区式还是功能式?
如果你的应用是由独立的,仅仅共享模型和配置的各组建组成,分区式将是个好选择。一个例子是允许用户建立网站的SaaS应用。你将会有独立的蓝图用于主页,控制面板,用户网站,和高亮面板。这些组建有着完全不同的静态文件和布局。如果你想要将你的蓝图提取成插件,或用之于别的项目,一个分区式架构将是正确的选择。另一方面,如果你的应用的组件之间的联系较为紧密,使用功能式架构会更好。
蓝图基本用法:
让我们看看来自Facebook例子的一个蓝图的代码:
facebook/views/profile.py
from flask import Blueprint, render_templateprofile = Blueprint('profile', __name__)@profile.route('/')def timeline(user_url_slug): # 做些处理 return render_template('profile/timeline.html')@profile.route('/ /photos')def photos(user_url_slug): # 做些处理 return render_template('profile/photos.html')@profile.route('/ /about')def about(user_url_slug): # 做些处理 return render_template('profile/about.html')
要想创建一个蓝图对象,你需要importBlueprint()
类并用参数name
和import_name
初始化。通常用__name__
,一个表示当前模块的特殊的Python变量,作为import_name
的取值。
假如使用分区式架构,你得告诉Flask某个蓝图是有着自己的模板和静态文件夹的。下面是这种情况下我们的定义大概的样子:
profile = Blueprint('profile', __name__, template_folder='templates', static_folder='static')
现在我们已经定义好了蓝图。是时候向Flask app注册它了(采用register_blueprint函数)。
facebook/init.py
from flask import Flaskfrom .views.profile import profileapp = Flask(__name__)app.register_blueprint(profile)
现在在fackbook/views/profile.py中定义的路径(比如/<user_url_slug>
)会被注册到应用中,就像是被通过@app.route()
定义的。
继续看Facebook的例子,注意到所有的个人信息路由都以<user_url_slug>
开头并把它传递给视图函数。我们想要用户通过类似的URL访问个人信息。通过给所有的蓝图的路由定义一个动态前缀,我们可以结束这种单调的重复。
蓝图允许我们定义静态的或动态的前缀。举个例子,我们可以告诉Flask蓝图中所有的路由应该以/profile作为前缀(在Blueprint函数中的参数url_prefix='/profile');这样是一个静态前缀。在Fackbook这个例子中,前缀取决于用户浏览的是谁的个人信息。他们在URL对应片段中输入的文本将决定我们输出的视图;这样是一个动态前缀。
我们可以选择何时定义我们的前缀。我们可以在下列两个时机中选择一个定义前缀:当我们实例化Blueprint()
类的时候,或当我们在app.register_blueprint()
中注册的时候。
下面我们在实例化的时候设置URL前缀:
facebook/views/profile.py
from flask import Blueprint, render_templateprofile = Blueprint('profile', __name__, url_prefix='/')# [...]
下面我们在注册的时候设置URL前缀:
facebook/init.py
from flask import Flaskfrom .views.profile import profileapp = Flask(__name__)app.register_blueprint(profile, url_prefix='/')
尽管这两种方式在技术上没有区别,最好还是在注册的同时定义前缀。这使得前缀的定义可以集中到顶级目录中。因此,我推荐使用url_prefix
。
我们可以在前缀中使用转换器(converters),就像调用route()一样。同样也可以使用我们定义过的任意自定义转换器。通过这样做,我们可以 自动处理在蓝图前缀中传递过来的值。在这个例子中,我们将根据URL片段获取用户类并传递到我们的profile蓝图中。我们将通过一个名为url_value_preprocessor()
装饰器来做到这一点。
facebook/views/profile.py
from flask import Blueprint, render_template, gfrom ..models import User# The prefix is defined in facebook/__init__.py.profile = Blueprint('profile', __name__)@profile.url_value_preprocessordef get_profile_owner(endpoint, values): query = User.query.filter_by(url_slug=values.pop('user_url_slug')) g.profile_owner = query.first_or_404()@profile.route('/')def timeline(): return render_template('profile/timeline.html')@profile.route('/photos')def photos(): return render_template('profile/photos.html')@profile.route('/about')def about(): return render_template('profile/about.html')
我们使用g
对象来储存个人信息的拥有者,而g可以用于Jinja2模板上下文。这意味着在这个简单的例子中,我们仅仅需要渲染模板,需要的信息就能在模板中获取。
facebook/templates/profile/photos.html
{% extends "profile/layout.html" %}{% for photo in g.profile_owner.photos.all() %}