Django模板:通过扩展模板覆盖包含子模板的块

37

我想知道是否有人知道如何处理下面这种奇怪的模板结构:

### base.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">

<head>
  <title> {% block title %} Title of the page {% endblock %} </title>
</head>

<body>
  <header>
    {% block header %}
      {% include "base/header.html" %}
    {% endblock header %}
  </header>
  {% block content %}{% endblock %}
</body>

</html>

### base/header.html
<div id="menu-bar">
  {% block nav %}
    {% include "base/nav.html" %}
  {% endblock %}
</div>

### base/nav.html
<nav id="menu">
  <ul>
    <li>
      <a href="/profile/">My Profile</a>
    </li>
    <li>
      <a href="/favs/">My Favorites</a>
    </li>
    {% block extra-content %}{% endblock %}
  </ul>
</nav>

而且,问题的关键在于:


### app/somepage.html
{% extends "base.html" %}
{% block content %}
  <p>Content is overridden!</p>
{% endblock %}

{% block extra-content %}
  <p>This will not show up, though...</p>
{% endblock %}

{% block nav %}
  <p>Not even this.</p>
{% endblock %}

问题在于当扩展一个模板时,你只能覆盖父级中声明的块,而不能覆盖其任何子级。

我想我可以将base.html制作成未使用的空嵌套块的外壳,以涵盖所有未来的情况,但即使如此也能正常覆盖吗?这是唯一的方法吗?

如果你想知道为什么我在base.html周围有一个双向包含/扩展工作流程,那是因为我有许多子模板希望在整个项目中使用:头部、脚注、导航、侧边栏等。它们在整个站点中的结构都是一致的,但在许多情况下,整个站点的一个子分区只需要这些子模板中的几个。我的想法是在templates/base文件夹下定义子模板,并有templates/base-type1.html、templates/base-type2.html等来扩展其他地方。每种类型都只引用所需的子模板,并覆盖它们以根据需要放置内容。


1
发布后,我在侧边栏看到了这个问题(https://dev59.com/Dmox5IYBdhLWcg3wsGWC),即使我已经彻底搜索了Stack Overflow和谷歌。我理解Django中这种限制的机制,但是,天啊,其影响令人沮丧。 - Chris Keele
完全尝试将此作为答案发布,但我的新堆栈帐户缺少声望...忘记了这一点。 - Chris Keele
对于未来的查找者:上述问题在stackoverflow上有一个基本的代码示例,几个答案下面展示了@Marcin的有用策略。 - Chris Keele
似乎我的备选策略,在base.html中预先定义所有块也行不通。 - Chris Keele
3个回答

37
似乎很少有人知道你可以使用with关键字和include一起使用,将变量传递到被包含模板的上下文中 - 你可以在被包含模板中使用它来指定包含内容。
# base.html
<html>
    <body>
        {% block header %}{% include "header.html" %}{% endblock %}
    </body>
</html>

# header.html
# some stuff here
<div id="header">
    <img src="logo.png">
    {% include nav_tmpl|default:"navigation.html" %}
</div>

# special_page.html (uses other navigation)
{% extends "base.html" %}
{% block header %}
    {% include "header.html" with nav_tmpl="special_nav.html" %}
    # you might also want to wrap the include in an 'if' tag if you don't want anything
    # included here per default 
{% endblock %}

这种方法可以避免为覆盖一个块而单独创建一个文件。你也可以使用 with 关键词来在更大范围的包含关系中传递值。


16

一个比@Bernhard Vallant提出的解决方案更加简洁的变体:

# base.html
<html>
    <body>
        {% block header %}{% include "header.html" %}{% endblock %}
    </body>
</html>

# header.html
# some stuff here
<div id="header">
    <img src="logo.png">
    {% include nav_tmpl|default:"navigation.html" %}
</div>

# special_page.html (uses other navigation)
{% extends "base.html" %}
{% block header %}
    {% with nav_tmpl="special_nav.html" %}
        {{ block.super }}
    {% endwith %}
{% endblock %}

6
您可以通过扩展您当前包含的模板来解决此问题,然后包含扩展而不是当前包含的基本模板。

2
如果我只有一个线性模板层次结构(就像上面的基本示例一样),那么您的解决方案可能有效。但在现实生活中,我的情况是一个分支层次结构,其中base.html是树干。正如简要提到的那样,它还包括页脚、侧边栏等;这意味着我不能简单地翻转包含方向并扩展头部。 - Chris Keele
1
如果我有一个header.html和footer.html,我会让它们各自扩展base.html,然后在example.html中...做什么?包括头部和底部?并在此基础上扩展base?很抱歉需要进一步澄清,但我现在离开了代码,“用派生模板替换include”对我来说很难想象,没有沙盒。 :) - Chris Keele
再加入第四个玩家,没问题。这很有道理,也很丑陋。我想我宁愿有一个不优雅的base.html和一个在角落里的子模板目录,也不愿用一堆模板存根来污染我的应用程序模板目录...我希望有一个折中的方法。除了我的项目背景之外,你更喜欢哪种方式? - Chris Keele
在我的情况下,我主要利用这个模板结构来创建一个高度样式化、可重用但可定制的框架,围绕着我的项目实际核心内容...这意味着基础部分有相当多的块和相当多的存根。但我喜欢这个技巧,下次我做一个我自己风格的项目时会记住它。谢谢! - Chris Keele
1
哎呀..这是那种在学会之后看起来非常简单,但在此之前似乎无比复杂的课程。谢谢! - Matthew Weber
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接