Flask 蓝图(Blueprint)

蓝图使用起来就像应用当中的子应用同样,能够有本身的模板,静态目录,有本身的视图函数和URL规则,蓝图之间互相不影响。可是它们又属于应用中,能够共享应用的配置。对于大型应用来讲,咱们能够经过添加蓝图来扩展应用功能,而不至于影响原来的程序。不过有一点要注意,目前Flask蓝图的注册是静态的,不支持可插拔。css

建立一个蓝图

比较好的习惯是将蓝图放在一个单独的包里,因此让咱们先建立一个”admin”子目录,并建立一个空的”__init__.py”表示它是一个Python的包。如今咱们来编写蓝图,将其存在”admin/admin_module.py”文件里:flask

from flask import Blueprint
 
admin_bp = Blueprint('admin', __name__)
 
@admin_bp.route('/')
def index(name):
    return '<h1>Hello, this is admin blueprint</h1>'

咱们建立了蓝图对象”admin_bp”,它使用起来相似于Flask应用的app对象,好比,它能够有本身的路由”admin_bp.route()”。初始化Blueprint对象的第一个参数’admin’指定了这个蓝图的名称,第二个参数指定了该蓝图所在的模块名,这里天然是当前文件。app

接下来,咱们在应用中注册该蓝图。在Flask应用主程序中,使用”app.register_blueprint()”方法便可:函数

from flask import Flask
from admin.admin_module import admin_bp
 
app = Flask(__name__)
app.register_blueprint(admin_bp, url_prefix='/admin')
 
if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

“app.register_blueprint()”方法的”url_prefix”指定了这个蓝图的URL前缀。如今,访问”http://localhost:5000/admin/”就能够加载蓝图的index视图了。优化

你也能够在建立蓝图对象时指定其URL前缀:this

admin_bp = Blueprint('admin', __name__, url_prefix='/admin')

这样注册时就无需指定:url

app.register_blueprint(admin_bp)

蓝图资源

蓝图有本身的目录,它的全部资源都在其目录下。蓝图的资源目录是由建立Blueprint对象时传入的模块名”__name__”所在的位置决定的。同时,咱们能够指定蓝图本身的模板目录和静态目录。好比咱们建立蓝图时传入:spa

admin_bp = Blueprint('admin', __name__,
                     template_folder='templates',
                     static_folder='static')

这样,该蓝图的模板目录就在”admin/templates”下,而静态目录就在”admin/static”下。固然,其实默认值就是这两个位置,不指定也不要紧。咱们能够用蓝图对象的”root_path”属性获取其主资源路径,”open_resource()”方法访问主资源路径下的某个文件,好比:debug

# 假设 current app 在路径 /home/bjhee/flask-app,
#这个将会返回 /home/bjhee/flask-app/admin
print admin_bp.root_path
# 读取文件 /home/bjhee/flask-app/admin/files/info.txt
with admin_bp.open_resource('files/info.txt') as f:
    info = f.read()
print info

构建URL

构建URL的方法”url_for()”。其第一个参数咱们称为端点(Endpoint),通常指向视图函数名或资源名。蓝图的端点名称都要加上蓝图名为前缀,建立Blueprint对象时的第一个参数是蓝图名字,当咱们经过端点名称来获取URL时,咱们要这样作:code

from flask import url_for
 
url_for('admin.index')                          # return /admin/
url_for('admin.static', filename='style.css')   # return /admin/static/style.css

这样才能得到’admin’蓝图下视图或资源的URL地址。若是,”url_for()”函数的调用就在本蓝图下,那蓝图名能够省略,但必须留下”.”表示当前蓝图:

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

蓝图在国际化中的使用

@app.route('/<lang_code>/')
def index(lang_code):
    g.lang_code = lang_code
    return '<h1>Index of language %s</h1>' % g.lang_code
 
@app.route('/<lang_code>/path')
def path(lang_code):
    g.lang_code = lang_code
    return '<h1>Language base URL is %s</h1>' % url_for('index', lang_code=g.lang_code)

每一个路由都要加”<lang_code>”参数,并且每一个视图函数都要将这个参数保存在上下文环境变量中以便其余地方使用,能不能简化呢?让咱们建立一个以参数作URL前缀的蓝图吧:

from flask import Blueprint, g, url_for
 
lang_bp = Blueprint('lang', __name__, url_prefix='/<lang_code>')
 
@lang_bp.route('/')
def index():
    return '<h1>Index of language %s</h1>' % g.lang_code
 
@lang_bp.route('/path')
def path():
    return '<h1>Language base URL is %s</h1>' % url_for('.index', lang_code=g.lang_code)

将上面的代码保存在”lang_module.py”中,而后在应用主程序里注册:

from lang_module import lang_bp
 
app.register_blueprint(lang_bp)

这样作的确省去了每一个路由加”<lang_code>”参数的麻烦,但若是运行了该程序,会发现报错。由于在视图中没有”lang_code”传进来,因此也没地方设置这个”g.lang_code”变量。这里,咱们就要用到URL预处理器了,让咱们回到蓝图代码”lang_module.py”,加上下面的函数:

@lang_bp.url_value_preprocessor
def get_lang_code_from_url(endpoint, view_args):
    g.lang_code = view_args.pop('lang_code')

这个”@lang_bp.url_value_preprocessor”装饰器代表,它所装饰的函数,会在视图函数被调用以前,URL路径被预处理时执行。并且只针对当前蓝图的全部视图有效。它所传入的第二个参数,保存了当前请求URL路径上的全部参数的值。因此,上面的”get_lang_code_from_url()”函数就能够在URL预处理时,设置”g.lang_code”变量。这样,视图函数中就能够取到”g.lang_code”,而咱们的程序也可以正常运行了。

还有能够优化的地方。每次调用”url_for()”来构建路径时,必须给”lang_code”参数赋上值。这个是否也能够统一处理?咱们再加上一个函数:

from flask import current_app
 
@lang_bp.url_defaults
def add_language_code(endpoint, values):
    if 'lang_code' in values or not g.lang_code:
        return
    if current_app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
        values['lang_code'] = g.lang_code

这个”@lang_bp.url_defaults”装饰器所装饰的函数,会在每次调用”url_for()”时执行,也只对当前蓝图内的全部视图有效。它就能够在构建URL时,设置url规则上参数的默认值,你只需将参数名及其默认值保存在函数的第二个参数values里便可。

”current_app.url_map.is_endpoint_expecting()”是用来检查当前的端点是否必须提供一个”lang_code”的参数值。由于咱们这个蓝图里的全部端点都包含前缀”<lang_code>”,这种状况下”is_endpoint_expecting”检查能够省去,因此上面的函数能够简化为:

@lang_bp.url_defaults
def add_language_code(endpoint, values):
    values.setdefault('lang_code', g.lang_code)

如今,咱们就能够将视图函数”url_for()”简写为:

@lang_bp.route('/path')
def path():
    return '<h1>Language base URL is %s</h1>' % url_for('.index')

最终代码

from flask import Blueprint, g, url_for
 
lang_bp = Blueprint('lang', __name__, url_prefix='/<lang_code>')
 
@lang_bp.route('/')
def index():
    return '<h1>Index of language %s</h1>' % g.lang_code
 
@lang_bp.route('/path')
def path():
    return '<h1>Language base URL is %s</h1>' % url_for('.index')

@lang_bp.url_value_preprocessor
def get_lang_code_from_url(endpoint, view_args):
    g.lang_code = view_args.pop('lang_code')

# 设置url规则上参数的默认值
@lang_bp.url_defaults
def add_language_code(endpoint, values):
    values.setdefault('lang_code', g.lang_code)
相关文章
相关标签/搜索