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

Posted

技术标签:

【中文标题】Django 模板:通过扩展模板覆盖包含的子模板块【英文标题】:Django templates: overriding blocks of included children templates through an extended template 【发布时间】:2012-04-17 07:03:11 【问题描述】:

我想知道是否有人知道如何处理以下古怪的模板结构:

### 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 等。每种类型只会引用所需的子模板,并根据需要覆盖它们以放置内容。

【问题讨论】:

好吧,发布后我看到this question 在侧边栏中弹出,即使在彻底搜索堆栈溢出和谷歌搜索之后也是如此。我了解 django 中这种限制的机制,但是伙计,其含义令人沮丧。 完全尝试将此作为答案发布,但我的新堆栈帐户缺少代表...忘记了。 对于未来的发现者:aforementioned question 有一个基本代码示例,其中包含几个答案,展示了@Marcin 的有用策略。 似乎我在 base.html 中预先定义所有块的替代策略也行不通。 【参考方案1】:

似乎鲜为人知的是,您可以使用 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 关键字通过更大的包含层次结构传递值。

【讨论】:

【参考方案2】:

solution proposed by @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 %

【讨论】:

【参考方案3】:

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

【讨论】:

如果我只有一个线性的模板层次结构,你的解决方案就可以工作,就像上面的基本示例一样。 IRL 我的情况是一个分支层次结构,其中 base.html 作为树的主干。如前所述,它还包括页脚、侧边栏等;这意味着我不能简单地翻转包含的方向并扩展标题。 @ChrisKeele 只要您的包含在一个块中,您就可以将包含替换为包含派生模板,所以可以。 所以如果我有一个 header.html 和一个 footer.html,我会让每个扩展 base.html,然后在 example.html... 做什么?包括页眉和页脚?并在此基础上扩展基础?很抱歉需要进一步澄清,但我现在远离代码,如果没有沙箱,我很难想象“用包含派生模板替换包含”。 :) @ChrisKeele 不,如果你想创建 example.html,扩展 base.html,并用你的 example_header.html 覆盖导入默认 header.html 的块,它本身派生自默认 header .html. Doh.. 这是其中一课,在你学会之后看起来很简单,但事先却如此复杂。谢谢!

以上是关于Django 模板:通过扩展模板覆盖包含的子模板块的主要内容,如果未能解决你的问题,请参考以下文章

django:在包含的模板中使用块

我们如何在 Django 中编写扩展多个父模板的子模板?

Django 模板扩展:子级不覆盖父级

在 if 条件内调用块:django 模板

在 django admin 中覆盖模板

django:在包含的模板中使用块