
作者 | 单雨
责编 | 屠敏
模板继承
简介
模板继承允许你建立一个基本的"骨架"模板, 它包含了网站中所有常见的元素,并定义了可以被子模板覆盖的 块(blocks) 。示例:
假如父模板base.html如下:
<!DOCTYPE html>
<htmllang="en">
<head>
<linkrel="stylesheet"href="style.css">
<title>{% block title %}My amazing site{% endblock %} </title>
</head>
<body>
<divid="sidebar">
{% block sidebar %}
<ul>
<li><ahref="/">Home </a></li>
<li><ahref="/blog/">Blog </a></li>
</ul>
{% endblock %}
</div>
<divid="content">
{% block content %}{% endblock %}
</div>
</body>
</html>
它定义了一个简单的 HTML 骨架文档, 假设这是一个简单的两列页面 。子模板的工作就是填充空的 块(block) 中的内容 。
在这个例子中, block 标签定义了三个可以被子模板填充的块 。block标签告诉了模板系统哪些地方可能被子模板覆盖 。例如 ,子模板可能如下:
{% extends "base.html" %}
{% block title %}My amazing blog{% endblock %}
{% block content %}
{% for entry in blog_entries %}
<h2>{{ entry.title }} </h2>
<p>{{ entry.body }} </p>
{% endfor %}
{% endblock %}
extends 标签告诉模板系统这个模板继承了另外的模板 。当模板系统对此模板进行运算时, 首先会寻找他的父模板 ——在这里是"base.html" 。
在这一点上, 模板引擎会在 base.html 中发现三个 block 标签, 并且使用子模板的内容替换掉这些块 。根据变量blog_entries 的值, 输出可能看起来像这样:
<!DOCTYPE html>
<htmllang="en">
<head>
<linkrel="stylesheet"href="style.css">
<title>My amazing blog </title>
</head>
<body>
<divid="sidebar">
<ul>
<li><ahref="/">Home </a></li>
<li><ahref="/blog/">Blog </a></li>
</ul>
</div>
<divid="content">
<h2>Entry one </h2>
<p>This is my first entry. </p>
<h2>Entry two </h2>
<p>This is my second entry. </p>
</div>
</body>
</html>
注意:因为子模板没有定义 sidebar 块, 那么父模板的内容就会被使用 。通常来说, 父模板 {% block %} 中的内容会被作为备用的内容 ,在子模板没有覆盖时就会被使用 。
多重继承
模板继承可以是多重继承 ,多重继承常见的模式是:
- 创建一个 base.html 模板把控网站的整体风格 。
- 为网站的每个子分类创建一个 baseSECTIONNAME.html模板. 比如, basenews.html, base_sports.html 。 这些模板都继承 base.html 模板 。这些模板中包含特定的设计/风格 。
- 为每一种类型的页面创建一个模板, 比如 news article 或blog 内容 。这些模板扩展上一级模板的相应分类 。
上述的关系可以用下图表示:
这样就能最大限度的重用模板代码 ,比如在所有页面通用的导航栏 。
模板继承注意事项
- {% extends %}必须位于模板的最开始 , 如果在其他的部分声明, 则不生效 。
- 在基础模板尽可能多的使用{%block%} ,子模板不需要定义所有父模板中的块, 所以你可以在若干的块中填充默认值, 然后定义之后需要自定义的块 , 有更多的可用块总是更好的 。
- 如果发现自己在许多模板中有重复内容的了, 这可能需要移动这些内容到父模板的 {% block %} 中 。
- 如果需要得到父模板块中的内容, 可以用{{ block.super }} 变量 。使用 {{ block.super }} 插入的数据不会被自动转义 ,因为它已经被转义了 。如果需要转义, 可以在父模板中转义 。
- 使用模板标签在{% block %}块外部创建的变量不能在块内使用 。例如 ,这个模板不渲染任何东西:
{% trans "Title"astitle %}
{% block content %}{{ title }}{% endblock %}
- 为了具有更好的可读性 ,也可以给{% endblock %}块标签定义一个名字 。例如:
{% block content %}
...
{% endblock content %}
在一个较长的模板中, 这个方法可以让你知道是哪一个{% block %} 标签定义结束了 。
- 不能在同一模板中定义多个具有相同名称的块标签 。存在这个限制是因为{%block%}标签是"双向"定义的 。也就是说, 它不仅指定了子模板要填充父模板的哪个块, 也说明了父模板要引用哪些子模板块的内容 。所以在子模板中有多个同名的{%block%}标签时, 父模板就不知道到底要引用子模板中哪个块的内容了 。
自动HTML转义
从模板直接生成HTML存在XSS风险
从模板生成HTML时, 总是有一种风险, 即一个变量将会影响生成的HTML字符 。考虑这个模板片段:
Hello, {{ name }}
看起来可能没有什么风险 ,但是如果用户输入的名字为一个HTML代码:
<>alert('hello')</>
当{{name}}为这个值时 ,模板将呈现为:
Hello, <>alert('hello')</>
这将使浏览器弹出一个弹出一个Java警告 。同样的 ,考虑另外一种情况:
如果名字中包含一个 '<' 符号:
<b>username
对应的模板将为:
Hello, <b>username
这意味着在此之后的文字将呈现为粗体 。
由此带来一个风险:
用户提交的数据是不可靠的且不应被直接插入到您的网页, 因为恶意用户可以使用这种潜在的漏洞做危害网站的事情 。这种类型的安全漏洞被称为 Cross Site ing (跨站脚本) (XSS) 攻击 。
使用转义避免XSS风险
(1)使用escape标签
确保让不受信任的变量经过了 escape 过滤器 , 将危险的HTML字符替换为无害的HTML转义字符 。但是这常常被忽略 。
(2)使用自动转义
在Django中, 默认每个模板会自动转义输出的每一个变量标签 。具体来说, 这五个字符会被转义:
- < 被替换为 <
- > 被替换为 >
- ' (单引号) 被替换为 '
- " (双引号) 被替换为 "
- & 被替换为 &
这种行为是默认的 。如果使用的是Django的模板系统, 自然拥有这种保护 。
关闭自动转义
可以在站点 ,模板和变量三个层级关闭自动转义 。
(1)对单个变量
要为一个单独的变量禁用自动转义, 使用 safe 过滤器(https://docs.djangoproject.com/zh-hans/2.2/ref/templates/builtins/#std:templatefilter-safe):
This will be escaped: {{ data}}
This will not be escaped: {{ data|safe }}
如果变量中包含“”字符 ,输出也是“” 。
(2)对于模板文本块
要在模板中控制自动转义, 可以在整个模板 (或者模板的特定区域) 使用 autoescape 标签, 如:
{% autoescape