Flask多蓝图模板目录冲突解决

分析

    1、app创建时会从Flask注册函数中读取template_folder,如果没有设置,默认是app/templates,作为全局jinja_loader
    2、render_template函数会首先访问app的全局jinja_loader,从中读取模板路径。
    3、访问不到就会循环访问所有注册蓝图的jinja_loader。由于循环访问的是所有蓝图的无序键值字典, 所以恰巧碰到其中一个蓝图下有同名模板文件,便会渲染这个模板文件。
    4、附上关键代码(/usr/lib/python2.6/site-packages/flask/templating.py)

def get_source(self, environment, template):
        for loader, local_name in self._iter_loaders(template):
            try:
                return loader.get_source(environment, local_name)
            except TemplateNotFound:
                pass
  
        raise TemplateNotFound(template)
  
    def _iter_loaders(self, template):
        loader = self.app.jinja_loader
        if loader is not None:
            yield loader, template
  
        # old style module based loaders in case we are dealing with a
        # blueprint that is an old style module
        try:
            module, local_name = posixpath.normpath(template).split('/', 1)
            blueprint = self.app.blueprints[module]
            if blueprint_is_module(blueprint):
                loader = blueprint.jinja_loader
                if loader is not None:
                    yield loader, local_name
        except (ValueError, KeyError):
            pass
        for blueprint in itervalues(self.app.blueprints):
            if blueprint_is_module(blueprint):
                continue
            loader = blueprint.jinja_loader
            if loader is not None:
                yield loader, template

解决办法

    1、官方给出的解决办法(妥协)
        将所有模板文件放在一起,按蓝图名字划分子目录。也就是全部都放在app的全局jinja_loader目录。访问的时候,使用render_template('blueprint_name/template_file.html')这种方式。参见:http://flask.pocoo.org/docs/0.10/blueprints/#templates
    2、其他友人的解决办法
        在创建app时修改全局jinja_loader,改为ChoiceLoader类型,使每个注册的蓝图loader都作为一个字典元素插入到ChoiceLoader中的PrefixLoader中,这样在访问时就可以在PrefixLoader中根据传进的蓝图名查找到当前loader路径。
        作者称这样的好处不用修改项目架构。不过这样也会有个问题:使用渲染函数时需要修改模板字符串,因为是PrefixLoader,需要添加delimiter标示,比如访问user下的index.html要这样render_template('user.index.html');参见:http://fewstreet.com/2015/01/16/flask-blueprint-templates.html 
    3、github上的commit
        这种方法修改了render_template函数,在渲染模板时判断是否是蓝图以及是否存在蓝图loader。如果存在优先使用蓝图自身的loader。这种方法比较干脆直接,参见:https://github.com/mitsuhiko/flask/pull/1537/files
    4、我的解决方式
        参照了github解决方法的思路,我在注册app时通过before_request修改全局jinja_loader,在收到请求时临时把当前蓝图的loder路径添加到全局jinja_loader的searchpath列表中,这样做的目的:①避免修改项目结构,②避免修改render_template参数,③避免修改框架代码,不方便迁移部署。

代码

def create_app():
    # 优先使用当前蓝图的模板
    @app.before_request
    def before_request():
        if request.blueprint is not None:
            bp = app.blueprints[request.blueprint]
            if bp.jinja_loader is not None:
                newsearchpath = bp.jinja_loader.searchpath + app.jinja_loader.searchpath
                app.jinja_loader.searchpath = newsearchpath
                # 以下为2016-03-11日更新:
                # 如果访问非蓝图模块或蓝图中没有指定template_folder,默认使用app注册时指定的全局template_floder.
            else:
                app.jinja_loader.searchpath = app.jinja_loader.searchpath[-1:]
        else:
            app.jinja_loader.searchpath = app.jinja_loader.searchpath[-1:]
    return app

 

 

 

转载于:https://my.oschina.net/yehun/blog/870928

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值