模板继承

Jinja 中最强大的部分就是模板继承。模板继承允许你构建一个包含你站点共同元素的基本模板“骨架”,并定义子模板可以覆盖的

听起来复杂,实际上很简单。从例子上手是最易于理解的。

基本模板

这个模板,我们会把它叫做 base.html ,定义了一个简单的 HTML 骨架文档,你可能使用一个简单的两栏页面。用内容填充空的块是子模板的工作:

  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
  2. <html lang="en">
  3. <html xmlns="http://www.w3.org/1999/xhtml">
  4. <head>
  5. {% block head %}
  6. <link rel="stylesheet" href="style.css" />
  7. <title>{% block title %}{% endblock %} - My Webpage</title>
  8. {% endblock %}
  9. </head>
  10. <body>
  11. <div id="content">{% block content %}{% endblock %}</div>
  12. <div id="footer">
  13. {% block footer %}
  14. &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.
  15. {% endblock %}
  16. </div>
  17. </body>

在本例中, {%block%} 标签定义了四个字幕版可以填充的块。所有的 block 标签告诉模板引擎子模板可以覆盖模板中的这些部分。

子模板

一个子模板看起来是这样:

  1. {% extends "base.html" %}
  2. {% block title %}Index{% endblock %}
  3. {% block head %}
  4. {{ super() }}
  5. <style type="text/css">
  6. .important { color: #336699; }
  7. </style>
  8. {% endblock %}
  9. {% block content %}
  10. <h1>Index</h1>
  11. <p class="important">
  12. Welcome on my awesome homepage.
  13. </p>
  14. {% endblock %}

{%extend%} 标签是这里的关键。它告诉模板引擎这个模板“继承”另一个模板。当模板系统对这个模板求值时,首先定位父模板。 extends 标签应该是模板中的第一个标签。它前面的所有东西都会按照普通情况打印出来,而且可能会导致一些困惑。更多该行为的细节以及如何利用它,见 Null-Master 退回

模板的文件名依赖于模板加载器。例如 FileSystemLoader 允许你用文件名访问其它模板。你可以使用斜线访问子目录中的模板:

  1. {% extends "layout/default.html" %}

这种行为也可能依赖于应用内嵌的 Jinja 。注意子模板没有定义 footer 块,会使用父模板中的值。

你不能在同一个模板中定义多个同名的 {%block%} 标签。因为块标签以两种方向工作,所以存在这种限制。即一个块标签不仅提供一个可以填充的部分,也在父级定义填充的内容。如果同一个模板中有两个同名的 {%blok%} 标签,父模板无法获知要使用哪一个块的内容。

如果你想要多次打印一个块,无论如何你可以使用特殊的 self 变量并调用与块同名的函数:

  1. <title>{% block title %}{% endblock %}</title>
  2. <h1>{{ self.title() }}</h1>
  3. {% block body %}{% endblock %}

Super 块

可以调用 super 来渲染父级块的内容。这会返回父级块的结果:

  1. {% block sidebar %}
  2. <h3>Table Of Contents</h3>
  3. ...
  4. {{ super() }}
  5. {% endblock %}

命名块结束标签

Jinja2 允许你在块的结束标签中加入的名称来改善可读性:

  1. {% block sidebar %}
  2. {% block inner_sidebar %}
  3. ...
  4. {% endblock inner_sidebar %}
  5. {% endblock sidebar %}

无论如何, endblock 后面的名称一定与块名匹配。

嵌套块和作用域

嵌套块可以胜任更复杂的布局。而默认的块不允许访问块外作用域中的变量:

  1. {% for item in seq %}
  2. <li>{% block loop_item %}{{ item }}{% endblock %}</li>
  3. {% endfor %}

这个例子会输出空的 <li> 项,因为 item 在块中是不可用的。其原因是,如果块被子模板替换,变量在其块中可能是未定义的或未被传递到上下文。

从 Jinja 2.2 开始,你可以显式地指定在块中可用的变量,只需在块声明中添加scoped 修饰,就把块设定到作用域中:

  1. {% for item in seq %}
  2. <li>{% block loop_item scoped %}{{ item }}{% endblock %}</li>
  3. {% endfor %}

当覆盖一个块时,不需要提供 scoped 修饰。

模板对象

Changed in version 2.4.

当一个模板对象被传递到模板上下文,你也可以从那个对象继承。假设调用代码传递 layout_template 布局模板到环境,这段代码会工作:

  1. {% extends layout_template %}

之前 layout_template 变量一定是布局模板文件名的字符串才能工作。