【问题标题】:Flask: Template in Blueprint Inherit from Template in App?Flask:蓝图中的模板继承自应用程序中的模板?
【发布时间】:2015-09-06 14:46:50
【问题描述】:

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

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

this other SO question 的答案让我觉得这两件事绝对应该如此。特别是答案的一部分:

如果有两个同名的模板[,一个]在app的templates文件夹和[另一个在]blueprint的template文件夹,那么app的templates文件夹中的模板优先。

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

在我的应用中,我有:

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/。其模板文件夹排列如下:

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

此包通过其init_app 类的init_app 函数(__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

但这不起作用,更糟糕的是——更令人惊讶的是——我的应用程序的页面被赋予了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() 函数经过精心设计,在返回任何蓝图的加载​​程序之前返回应用程序的加载程序。 (如果我正确阅读了几个这不是我当前的问题。)

这个结果让我更难理解为什么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,我希望我不需要这样做。

【问题讨论】:

    标签: python templates flask jinja2


    【解决方案1】:

    答案是:

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

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

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

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

    因为我已经产生了创建自己的base.html 模板的想法已经加载了最初继承自flask_user/templates/base.htmlflask_user 页面(login.html),所以有已经在我介绍myapp/templates/base.html 时,缓存中已经有一个名为base.html 的模板。

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

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

    【讨论】:

      【解决方案2】:

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

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

      您可以通过此链接 - 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

      【讨论】:

      • 我不愿意将 flask_user 包的部分内容拉到我的应用程序的层次结构中,原因如下:(1)作为已安装的 PyPI 包,我应该认为它应该可以在多个应用。确实,难道不是设计就是这样吗? (2) 将它的一部分复制到我的应用程序中会违反 DRY;和 (3) 除了这个模板继承问题(这似乎是flask 的问题而不是flask_user),它似乎大部分都有效。谁知道如果我到处移动它会破坏什么?在那种情况下,我的烧瓶福几乎肯定会太虚弱而无法救我。
      • 我完全同意你的担忧#2(关于 DRY)。我更新了我的答案并添加了选项#2
      • 我看不出选项 2 有什么帮助。它仍然将flask_user 包移动到我的myapp 文件夹中,而不是将它留在它所属的/usr/local/lib/python2.7/dist-packages 中......
      • 另外,关于来自几个street.com 的链接文章:我对_iter_loaders() 代码的阅读是,它应该在返回任何蓝图的加载​​器之前返回我的应用程序的jinja_loader。这似乎应该导致所引用的答案所承诺的完全正确,即“应用程序模板文件夹中的模板将获得优先权”。我想如果 _iter_loaders() 仅被调用以响应最外层的 render_template() 调用,但 not 对于已加载模板中 extends 语句引用的模板,这将崩溃。但这很愚蠢,对吧?
      猜你喜欢
      • 1970-01-01
      • 2019-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-22
      • 1970-01-01
      • 2021-11-23
      相关资源
      最近更新 更多