Flask:蓝图中的模板继承自应用程序中的模板?

Flask: Template in Blueprint Inherit from Template in App?

我是一个 Flask/Jinja2 新手,所以也许我忽略了一些明显的东西,但是:

开箱即用的 Flask 不应该允许存在于蓝图 templates/ 文件夹中的模板扩展由我的应用的 templates/ 文件夹定义的基本模板吗?即使蓝图还包含一个 "default" 基本模板(我通过定义我自己的同名基本模板来覆盖它),这不应该起作用吗?

this other SO question 的答案让我认为这两件事绝对应该如此。具体来说,答案的一部分是:

If there are two templates with same name[, one] in app's templates folder and [the other in] blueprint's template folder, then template in app's templates folder will get priority.

但对我来说完全不是这样。事实上,它似乎以 相反 的方式工作,即 blueprint 中的 base.html 被中定义的页面拉入我的应用程序,即使我的应用程序定义了自己的 base.html(如果上述答案正确,它应该 "get priority")。

在我的应用程序中我有:

myapp/
   templates/
       base.html
       pages/
           page_base.html
           home_page.html

其中 pages/home_page 扩展了 pages/page_base,后者又扩展了 base

我还在使用 PyPI 的 flask_user 包,它是(由 pip)安装在 /usr/local/lib/python2.7/dist-packages/flask_user/ 的。其templates文件夹排列如下:

flask_user/
    templates/
        base.html
        flask_user/
            [templates that extend base.html]

此包通过其 UserManager class (__init__.py, line 154):

    # Add flask_user/templates directory using a Blueprint                  
    blueprint = Blueprint('flask_user', 'flask_user', template_folder='templates')
    app.register_blueprint(blueprint)

我最初的想法是,通过定义我自己的 myapp/templates/base.html,我可以自定义从 flask_user/templates/flask_user/ 中的模板呈现的页面,使其看起来像我应用程序中的其他页面,因为(根据引用回答)我的 base.html 应该优先于 flask_userbase.html

但这不起作用,更糟糕的是 - 更令人惊讶的是 my 应用程序的页面被赋予了 flask_user 的默认外观页数。

深入挖掘...

查看@Pralhad Narsinh Sonar 的建议,即模板搜索路径的排序可能存在问题,这可能是由 DispatchingJinjaLoader._iter_loaders() 中建议的非确定性行为引起的 fewstreet.com article 他引用,我做了一个快速实验,看看 _iter_loaders() 会为我的应用程序产生什么顺序:

>>> from myapp.top import app, db
>>> from myapp.startup import init_app.init_app
>>> init_app(app, db)
>>> app.jinja_env.loader
<flask.templating.DispatchingJinjaLoader object at 0x7f233e396dd0>
>>> for loader in app.jinja_env.loader._iter_loaders('pages/home_page.html') :
...   print loader, loader.searchpath
... 
<jinja2.loaders.FileSystemLoader object at 0x7f233eb08490> ['/var/www/python/myapp/templates']
<jinja2.loaders.FileSystemLoader object at 0x7f233e36ef10> ['/usr/local/lib/python2.7/dist-packages/flask_user/templates']

正如预期的那样,迭代器首先为我的应用程序 templates/ 文件夹 生成加载器,然后为 flask_user/templates/ 生成加载器。事实上,_iter_loaders() 函数在 returning any 蓝图的加载​​器之前被特意构造为 return 应用程序的加载器。 (如果我正确阅读 fewstreet.com 文章,它关注的问题是 多个 蓝图之间的非确定性排序,因为只有一个蓝图被我的应用 -- 不是我当前的问题。)

这个结果让我更难理解为什么 flask_userbase.html 模板被用来解析我模板的 {% extends "base.html" %} 语句*。鉴于我在 myapp/templates 中有自己的 base.html 文件,我认为模板系统没有任何理由查看 flask_user/templates 中的任何内容以呈现 myapp/templates/pages/home_page.html.

* 出于测试目的,我通过上面提到的 pages/page_base.html 摆脱了间接寻址。

所以: 显然其他地方出了问题,但是什么

我还没有深入了解 flask/templating.pyjinja2/loaders.py 中的相关代码,无法理解为什么以及如何发生这种情况。这是我第一次涉足 Flask,我希望我不需要。

如果您能分享代码的 blueprint 部分,将会非常有帮助。

这可能是搜索路径出现故障的结果 - 其中它将第一个在线模板视为要显示的模板。为避免这种情况,您始终可以唯一地重命名模板文件。如果出于任何限制或偏好,如果您想保留相同的名称,那么您可以尝试将模板文件保留在主模板文件夹的子目录下。

你可以通过这个 link - http://flask.pocoo.org/docs/0.10/blueprints/

选项#1

请尝试使用这种文件夹结构 - 您还需要在 blueprints -

中进行更改
myapp/
----templates/
--------base.html
--------pages/
------------page_base.html
------------home_page.html
----flask_user/
--------pages/
------------flask_user
----------------page_base.html
----------------home_page.html

更多关于烧瓶中这个问题的说明 - http://fewstreet.com/2015/01/16/flask-blueprint-templates.html

选项#2

myapp/
----templates/
--------base.html
--------pages/
------------page_base.html
------------home_page.html
--------flask_user
------------page_base.html
------------home_page.html
--------module_1
------------page_base.html
------------home_page.html
--------module_2
------------page_base.html
------------home_page.html
----flask_user/ # --> this is a module
-------- another module functionality
----module_1/ # --> this is a module
-------- another module functionality
----module_2/ # --> this is a module
-------- another module functionality

在上面的结构中,每个模块都有自己的路由文件,即 - view.py,因此单独的 blueprint 配置使每个功能模块化。

请看https://www.digitalocean.com/community/tutorials/how-to-structure-large-flask-applications

答案是:

一直以来,我一直在 运行(并重新加载)我的应用 debug=True

这非常适合自动重新加载已更改的 Python 模块。

但是对于更改的模板?嗯……没那么多。

在我的 home_page.html 模板中引入断点并使用 Flask 调试器回顾几个堆栈帧后,我发现 Jinja2 使用 LRU 缓存来存储(按名称)它具有的模板已经解析了。

因为我萌生了创建自己的 base.html 模板的想法 已经加载 flask_user 页面 (login.html) 之后,最初继承自flask_user/templates/base.html,当我引入myapp/templates/base.html时,缓存中已经一个名为base.html的模板。

所以我停止并重新启动应用程序,现在我的 home_page.htmlflask_userlogin.html 都正确地继承自 my base.html 而不是来自 flask_userbase.html。我怀疑在我重新启动该应用程序之前,我自己的 base.html 甚至从未被我的应用程序的模板加载器 读取

这是一个相当重要的——我相信,未记录——新手必须弄清楚的陷阱。我就把它留在这里,希望有一天它能帮助碰巧进入这个特定陷阱的其他人。