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

Hep*_*tus 5 python templates jinja2 flask

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

开箱即用的 Flask 是否应该允许存在于蓝图templates/文件夹中的模板扩展由我的应用程序templates/文件夹定义的基本模板?即使蓝图还包含一个“默认”基本模板,我通过定义自己的同名基本模板来覆盖它,这不应该工作吗?

另一个 SO 问题的答案让我认为这两件事绝对应该如此。特别是答案的一部分:

如果应用程序的模板文件夹中有两个同名的模板[,一个] 和蓝图的模板文件夹中的[另一个],则应用程序模板文件夹中的模板将获得优先权。

但这对我来说根本不起作用。事实上,它似乎工作的相反的方式,即base.html蓝图正由我的应用程序定义的页面拉,即使我的应用程序定义了自己的base.html(这应该“得到优先权”如果以上的答案是正确的)。

在我的应用程序中,我有:

myapp/
   templates/
       base.html
       pages/
           page_base.html
           home_page.html
Run Code Online (Sandbox Code Playgroud)

其中pages/home_pageextends pages/page_base,反过来extends base

我也在使用flask_user来自 PyPI的包,它是安装pip/usr/local/lib/python2.7/dist-packages/flask_user/. 其模板文件夹排列如下:

flask_user/
    templates/
        base.html
        flask_user/
            [templates that extend base.html]
Run Code Online (Sandbox Code Playgroud)

该包通过在init_appUserManager类的函数(__init__.py第 154 行)中通过以下调用建立的蓝图将其模板提供给使用它的应用程序:

    # Add flask_user/templates directory using a Blueprint                  
    blueprint = Blueprint('flask_user', 'flask_user', template_folder='templates')
    app.register_blueprint(blueprint)
Run Code Online (Sandbox Code Playgroud)

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

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

深层发掘...

查看@Pralhad Narsinh Sonar 的建议,即模板搜索路径的排序可能存在问题,这可能是由他引用DispatchingJinjaLoader._iter_loaders()somestreet.com 文章中建议的非确定性行为引起的,我做了一个快速实验,看看是什么排序_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']
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,迭代器得到我的应用程序的加载程序templates/文件夹第一,产生了装载机前flask_user/templates/。事实上,该_iter_loaders()函数经过精心设计,在返回任何蓝图的加载器之前返回应用程序的加载器。(如果我正确阅读了 somestreet.com 文章,那么它所关注的问题是多个蓝图之间的非确定性排序,因为我的应用程序只使用一个蓝图,这不是我当前的问题。)

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

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

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

我还没有了解足够的相关代码,flask/templating.pyjinja2/loaders.py没有了解这可能发生的原因和方式。这是我第一次涉足 Flask,我希望我不需要这样做。

Hep*_*tus 4

答案是:

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

这对于自动重新加载更改的 Python 模块非常有用。

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

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

因为我在已经加载了一个page( )之后base.html就萌生了创建自己的模板的想法,该页面最初是继承自 的,所以在我引入时,缓存中已经有一个命名的模板了。flask_userlogin.htmlflask_user/templates/base.htmlbase.htmlmyapp/templates/base.html

所以我停止并重新启动了应用程序,现在 myhome_page.htmlflask_user's都正确地从my而不是从'slogin.html继承。我怀疑在我重新启动应用程序之前,我自己的应用程序的模板加载器从未读取过我自己的应用程序。 base.htmlflask_userbase.htmlbase.html

对于新手来说,这是一个相当重要的——而且,我相信,没有记录的——必须弄清楚的问题。我将其留在这里,希望有一天它能帮助其他碰巧踏入这个特定陷阱的人。