嵌套的Django模板
这看起来是个很基础的事情,但尽管我已经使用Django大约一年了,我还是没遇到过这种情况。
在很多模板或网页框架中,模板继承的工作方式有点不同,通常它们更像是包装器。如果你有一个子模板 `childtemplate.html`、一个父模板 `parenttemplate.html` 和一个祖父模板 `grandparenttemplate.html`,那么最终渲染出来的内容通常看起来像这样:
grandparent header
parent header
child header
child content
parent content
parent footer
grandparent content
grandparent footer
在Django中并不是完全这样,但我想知道如何实现它。
具体来说,我有一个“子”模板,假设它是 foo.html
。foo.html
可以选择性地接收一个变量 parent_template
,如果没有提供则默认使用 "base.html"。
{% extends parent_template|default:"base.html" %}
{% block content %}
I am a foo and my title is {{ foo.title }}
{% endblock content %}
所以,这就是我遇到的问题。如果 parent_template
是一个模板,那么这个模板应该把 foo.html
的内容包裹起来,然后把结果放到 base.html
中:
{% extends "base.html" %}
{% something_magical_here %}
<div>parent header</div>
# content of foo.html
<div>parent footer</div>
{% end_something_magical_here %}
然后在 base.html
中:
<html>
... snip ...
<div id="content">
{% something_else_magical %}
# content of parent_template rendering, or just foo.html if no parent_template given
{% end_something_else_magical %}
这应该渲染成
<html>
... snip ...
<div id="content">
<div>parent header</div>
I am a foo and my title is Bar
<div>parent footer</div>
如果设置了 parent_template,并且
<html>
... snip ...
<div id="content">
I am a foo and my title is Bar
如果没有设置。
我希望我的问题能说清楚:我需要(可选地)把一个模板包裹在父模板中,然后把结果发送到 base.html
模板。
通常,像这样的代码可能会有效:
#foo.html
{% extends "parent.html" %}
{% block child_content %}
I am a foo and my title is {{ foo.title }}
{% endblock child_content %}
#parent.html
{% extends "base.html" %}
{% block content %}
parent header
{% block child_content %}{% endblock child_content %}
parent content
parent footer
#base.html
base header
{% block content %}{% endblock content %}
base content
base footer
然而,由于 parent_template 可能是空的,有时候 base.html 只会得到 child_content
这个块,而不是 content
这个块。
另外,我希望能做到这一点,而不需要创建一堆子块(如果我决定 foo
应用应该有自己的 /foo/base.html
,然后再调用 /base.html
呢)?
有什么想法吗?
3 个回答
我有三个模板:gparent(祖父),parent(父亲)和gchild(孩子)。然后我测试了gparent和gchild,测试方法如下:
gparent里有一个hn标签的块,gchild继承了gparent。这里没有提到parent。
{% block hn %}
Can you see me? # 1 naked text in the html, treated as a string.
{{ "No, I cant see you." }} # 2 A string literal in the variable template brace.
{% endblock hn %}
这两个标签之间的内容在孩子的页面上都显示出来了。当后面的内容不是字符串时,Django会报解析错误。这是因为在双大括号里面,Django期待的是一个变量,或者是它可以计算的字符串。
当我在gchild里添加了一个hn块和一些内容后,我看到的只是gchild的hn块内容。
{% block hn %}
Now I have my own hn block
{% endblock hn %}
然后我重复了这个测试,保留了gchild的hn块,但去掉了内容:
{% block hn %}
{% endblock hn %}
一个空的但存在的hn块在gchild里覆盖了gparent的hn块内容。
接下来我在gparent和gchild之间插入了parent。现在parent继承了gparent,而gchild继承了parent。
{% block metadata %}
{{ "You must eat your metadata to grow big and strong" }}
{% endblock metadata %}
parent现在有这个块元数据标签,而gparent和gchild里都没有这个标签。这个内容没有显示出来。
接下来我在parent的hn块里嵌套了块元数据。gparent里仍然有我们刚才测试的两个字符串,gchild有一个空的hn块。
和之前一样,什么都没有显示。
现在我将gchild里的hn块去掉:
You must eat your metadata to grow big and strong
所以,你可以添加一个在早期模板中不存在的新块标签,只要它完全嵌套在一个祖先定义的可用块里面。
中间这一代会把这个新标签和它的内容传递给孩子,孩子会显示它,前提是孩子没有覆盖它。
记住,文档里把标签之间的内容描述为一个需要填充的空洞。如果孩子没有这个标签,空洞就会被父亲填充。这就是父亲内容成为默认内容的原因。这也是为什么大多数基础模板包含你希望在整个网站上使用的头部、底部、导航和样式。但如果孩子有相同的标签,孩子就自己填充了这个空洞。这也是为什么孩子可以有自己的块标题标签,即使它嵌套在html里面,通常是在父块头部里面,或者你选择称之为的任何东西。
当我第一次学习Django时,我对此感到很困惑,因为我总是想做一些疯狂的事情,比如
{% block first %}
{% block second %}
{% endblock first %}
{% endblock second %}
这在任何地方都行不通。
- base.html
{% block content %}
<p>Grand Parent file</p>
{% endblock %}
- parent.html
{% extends 'base.html' %}
{% block content %}
{% include 'grandchild1.html' %}
{% include 'grandchild2.html' %}
{% endblock %}
3.1. grandchild1.html
<div>Hello I'm grandchild 1</div>
3.2 grandchild2.html
<div>Hello I'm grandchild 2</div>
这样你可以把文件嵌套到任何层级。把每个部分写在不同的 .html 文件中。
然后把它们包含到 parent.html 或其他文件中。
这个 extends 模板标签可以接受一个可变的参数。
所以:
base.html
{% block content %}
<p>BASE</p>
{% endblock %}
parent.html
{% extends "base.html" %}
{% block content %}
{{ block.super }}
<p>PARENT</p>
{% endblock %}
foo.html
{% extends ext_templ %}
{% block content %}
{{ block.super }}
<p>FOO</p>
{% endblock %}
使用 base:
return render_to_response('foo.html', {'ext_templ':'base.html'})
会给你:
<p>BASE</p>
<p>FOO</p>
使用 parent:
return render_to_response('foo.html', {'ext_templ':'parent.html'})
会给你:
<p>BASE</p>
<p>PARENT</p>
<p>FOO</p>
补充:
解决这个问题以获得嵌套块的一种方法是:
base.html
{% block content %}
{% block top %}
<p>BASE START</p>
{% endblock %}
{% block bot %}
<p>BASE END</p>
{% endblock %}
{% endblock %}
parent.html
{% extends "base.html" %}
{% block top %}
{{ block.super }}
<p>PARENT</p>
{% endblock %}
{% block bot %}
<p>PARENT</p>
{{ block.super }}
{% endblock %}
foo.html
{% extends ext_templ %}
{% block top %}
{{ block.super }}
<p>FOO</p>
{% endblock %}
{% block bot %}
<p>FOO END</p>
{{ block.super }}
{% endblock %}
我想到的另一种方法是用一个 {% if ext_templ == 'parent.html' %}
标签来包裹这些块,但这似乎不太简洁。我也很想知道其他人对嵌套块的看法。