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_user
的 base.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_user
的 base.html
模板被用来解析我模板的 {% extends "base.html" %}
语句*。鉴于我在 myapp/templates
中有自己的 base.html
文件,我认为模板系统没有任何理由查看 flask_user/templates
中的任何内容以呈现 myapp/templates/pages/home_page.html
.
* 出于测试目的,我通过上面提到的 pages/page_base.html
摆脱了间接寻址。
所以: 显然其他地方出了问题,但是什么?
我还没有深入了解 flask/templating.py
或 jinja2/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.html
和 flask_user
的 login.html
都正确地继承自 my base.html
而不是来自 flask_user
的 base.html
。我怀疑在我重新启动该应用程序之前,我自己的 base.html
甚至从未被我的应用程序的模板加载器 读取。
这是一个相当重要的——我相信,未记录——新手必须弄清楚的陷阱。我就把它留在这里,希望有一天它能帮助碰巧进入这个特定陷阱的其他人。
我是一个 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_user
的 base.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_user
的 base.html
模板被用来解析我模板的 {% extends "base.html" %}
语句*。鉴于我在 myapp/templates
中有自己的 base.html
文件,我认为模板系统没有任何理由查看 flask_user/templates
中的任何内容以呈现 myapp/templates/pages/home_page.html
.
* 出于测试目的,我通过上面提到的 pages/page_base.html
摆脱了间接寻址。
所以: 显然其他地方出了问题,但是什么?
我还没有深入了解 flask/templating.py
或 jinja2/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.html
和 flask_user
的 login.html
都正确地继承自 my base.html
而不是来自 flask_user
的 base.html
。我怀疑在我重新启动该应用程序之前,我自己的 base.html
甚至从未被我的应用程序的模板加载器 读取。
这是一个相当重要的——我相信,未记录——新手必须弄清楚的陷阱。我就把它留在这里,希望有一天它能帮助碰巧进入这个特定陷阱的其他人。