在Django模板中,"{% extends %}"和"{% include %}"的含义分别是继承和包含。

128
我想在两个不同的基础文件中提供相同的内容。
因此,我正在尝试做到这一点:

page1.html:

{% extends "base1.html" %}
{% include "commondata.html" %}

page2.html:

{% extends "base2.html" %} 
{% include "commondata.html" %}

问题在于我似乎无法同时使用“extends”和“include”。有没有什么方法可以做到这一点?如果不行,那我怎样才能实现上述目标?
“commondata.html”覆盖了在“base1.html”和“base2.html”中都指定的一个块。
其目的是在“pdf”和“html”格式中提供相同的页面,其中格式略有不同。以上问题虽然简化了我试图做的事情,但如果我能得到答案,它将解决我的问题。
8个回答

131
当你使用extends模板标签时,你要表明当前模板继承于另一个模板,即它是一个子模板,依赖于一个父模板。Django将查看你的子模板并使用其内容填充父模板。
想要在子模板中使用的所有内容都应该在块内,Django使用这些块来填充父模板。如果你想在子模板中使用include语句,则必须将其放置在块中,以便Django理解它。否则它就毫无意义,并且Django不知道该如何处理它。
Django文档提供了一些非常好的示例,说明如何使用块替换父模板中的块。 https://docs.djangoproject.com/en/dev/ref/templates/language/#template-inheritance

1
我的commondata.html文件中已经定义了该块。但它并没有替换父模板的块...如果我不使用include,而是在page1.html和page2.html中都写入相同的数据,那当然可以工作。但我想将这种共性因素提取到commondata.html中。 - Net Citizen
好像可行,我记得以前试过这个,但可能当时打错字或者其他原因导致它不能正常工作。 - Net Citizen
1
请看下面的回答,我将解释为什么第一次对我没用。然而,我还是会把接受的回答留给你,因为你正确地回答了我提出的问题。 - Net Citizen

86

来自Django文档:

include标签应该被视为“呈现此子模板并包含HTML”的实现,而不是“解析此子模板并将其内容包含在父级中”。这意味着包含的模板之间没有共享状态 - 每个包含都是完全独立的渲染过程。

所以Django不会从commondata.html中获取任何块,并且它不知道如何处理块外呈现的HTML。


45
这应该能为您解决问题:将include标签放在块节内部。 page1.html:
{% extends "base1.html" %}

{% block foo %}
   {% include "commondata.html" %}
{% endblock %}

page2.html:

{% extends "base2.html" %}

{% block bar %}
   {% include "commondata.html" %}
{% endblock %}

这里的'foo'或'bar'块有什么重要意义?您在基本模板中引用它们吗?我很困惑为什么这会起作用。 - AlxVallejo

13

如果有助于未来的人理解,我希望提供有关为什么它对我不起作用的更多信息:

之所以它不能正常工作是因为Django中的{% include %}不喜欢类似花体撇号之类的特殊字符。 我试图包含的模板数据是从Word中复制粘贴的。 我必须手动删除所有这些特殊字符,然后才能成功包含。


3
您无法从包含文件中提取块并将其放入子模板以覆盖父模板的块。但是,您可以在变量中指定父级,并在上下文中指定基本模板。来自documentation
{% extends variable %}使用variable的值。如果变量求值为字符串,则Django将使用该字符串作为父模板的名称。如果变量求值为Template对象,则Django将使用该对象作为父模板。在“commondata.html”的顶部放置{% extends base_template %},而不是单独的“page1.html”和“page2.html”。然后在您的视图中,将base_template定义为“base1.html”或“base2.html”。

2
2015年12月10日编辑:正如评论中指出的那样,自1.8版本以来ssi已经被弃用。根据文档:

此标签已被弃用,并将在Django 1.10中删除。请使用include标签。


在我看来,这个问题的正确(最佳)答案是podshumok的答案,因为它解释了当与继承一起使用时include的行为原理。

然而,我有点惊讶于没有人提到Django模板系统提供的ssi标签,该标签专门用于内联包含外部的文本。在这里,“内联”意味着外部文本不会被解释、分析或插值,而只是简单地“复制”到调用模板中。

请参阅文档以获取更多详细信息(请确保在页面右下角的选择器中检查您适当的Django版本)。

https://docs.djangoproject.com/en/dev/ref/templates/builtins/#ssi

从文档中:

ssi
Outputs the contents of a given file into the page.
Like a simple include tag, {% ssi %} includes the contents of another file
– which must be specified using an absolute path – in the current page

同时要注意这种技术的安全隐患以及所需的ALLOWED_INCLUDE_ROOTS定义,必须添加到您的设置文件中。


1
注意,从1.8版本开始,ssi已被Include取代。https://docs.djangoproject.com/en/1.8/ref/templates/builtins/#std:templatetag-include - Tim S.

2

为了方便未来通过谷歌搜索到这篇文章的人,我提供以下参考:对于此类情况,您可能需要查看 Mezzanine 库提供的 {% overextend %} 标签。


1

在一个模板中,你可以同时使用 {% extends %} 和 {% include %}。

例如,下面的 base.html 显示了 Base

# "templates/base.html"

<p>Base</p>

{% block content %}{% endblock %}

此外,下面有一个名为additional.html的文件,其中显示了Additional

# "templates/additional.html"

<p>Additional</p>

而且,下面有一个index.html,显示Index

# "templates/index.html"

<p>Index</p>

现在,您可以扩展base.html并在{% block %}中包含additional.html,除了<p>Index</p>index.html中如下所示,然后使用Django View呈现index.html

# "templates/index.html"

{% extends "base.html" %}

{% block content %}
    <p>Index</p>
    {% include "additional.html" %}
{% endblock %}

然后,BaseIndexAdditional如下所示显示:

Base

Index

Additional

注意,在 {% block %} 之外的代码以及 {% block %} 内部 base.html 中没有的代码在 index.html 中会被忽略:

# "templates/index.html"

{% extends "base.html" %}

<p>Index</p>
{% include "additional.html" %}

{% block test %}
    <p>Index</p>
    {% include "additional.html" %}
{% endblock %}

因此,只显示如下所示的Base

Base

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