详解Python的Django框架中的模版继承


Posted in Python onJuly 16, 2015

在实际应用中,你将用 Django 模板系统来创建整个 HTML 页面。 这就带来一个常见的 Web 开发问题: 在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代码?

解决该问题的传统做法是使用 服务器端的 includes ,你可以在 HTML 页面中使用该指令将一个网页嵌入到另一个中。 事实上, Django 通过刚才讲述的 {% include %} 支持了这种方法。 但是用 Django 解决此类问题的首选方法是使用更加优雅的策略—— 模板继承 。

本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。

让我们通过修改 current_datetime.html 文件,为 current_datetime 创建一个更加完整的模板来体会一下这种做法:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
  <title>The current time</title>
</head>
<body>
  <h1>My helpful timestamp site</h1>
  <p>It is now {{ current_date }}.</p>

  <hr>
  <p>Thanks for visiting my site.</p>
</body>
</html>

这看起来很棒,但如果我们要为第三章的 hours_ahead 视图创建另一个模板会发生什么事情呢?

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
  <title>Future time</title>
</head>
<body>
  <h1>My helpful timestamp site</h1>
  <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>

  <hr>
  <p>Thanks for visiting my site.</p>
</body>
</html>

很明显,我们刚才重复了大量的 HTML 代码。 想象一下,如果有一个更典型的网站,它有导航条、样式表,可能还有一些 JavaScript 代码,事情必将以向每个模板填充各种冗余的 HTML 而告终。

解决这个问题的服务器端 include 方案是找出两个模板中的共同部分,将其保存为不同的模板片段,然后在每个模板中进行 include。 也许你会把模板头部的一些代码保存为 header.html 文件:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>

你可能会把底部保存到文件 footer.html :

<hr>
  <p>Thanks for visiting my site.</p>
</body>
</html>

对基于 include 的策略,头部和底部的包含很简单。 麻烦的是中间部分。 在此范例中,每个页面都有一个 <h1>My helpful timestamp site</h1> 标题,但是这个标题不能放在 header.html 中,因为每个页面的 <title> 是不同的。 如果我们将 <h1> 包含在头部,我们就不得不包含 <title> ,但这样又不允许在每个页面对它进行定制。 何去何从呢?

Django 的模板继承系统解决了这些问题。 你可以将其视为服务器端 include 的逆向思维版本。 你可以对那些 不同 的代码段进行定义,而不是 共同 代码段。

第一步是定义 基础模板 , 该框架之后将由 子模板 所继承。 以下是我们目前所讲述范例的基础模板:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
  <title>{% block title %}{% endblock %}</title>
</head>
<body>
  <h1>My helpful timestamp site</h1>
  {% block content %}{% endblock %}
  {% block footer %}
  <hr>
  <p>Thanks for visiting my site.</p>
  {% endblock %}
</body>
</html>

这个叫做 base.html 的模板定义了一个简单的 HTML 框架文档,我们将在本站点的所有页面中使用。 子模板的作用就是重载、添加或保留那些块的内容。 (如果你一直按顺序学习到这里,保存这个文件到你的template目录下,命名为 base.html .)

我们使用一个以前已经见过的模板标签: {% block %} 。 所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。 每个{% block %}标签所要做的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。

现在我们已经有了一个基本模板,我们可以修改 current_datetime.html 模板来 使用它:

{% extends "base.html" %}

{% block title %}The current time{% endblock %}

{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}

再为 hours_ahead 视图创建一个模板,看起来是这样的:

{% extends "base.html" %}

{% block title %}Future time{% endblock %}

{% block content %}
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
{% endblock %}

看起来很漂亮是不是? 每个模板只包含对自己而言 独一无二 的代码。 无需多余的部分。 如果想进行站点级的设计修改,仅需修改 base.html ,所有其它模板会立即反映出所作修改。

以下是其工作方式。 在加载 current_datetime.html 模板时,模板引擎发现了 {% extends %} 标签, 注意到该模板是一个子模板。 模板引擎立即装载其父模板,即本例中的 base.html 。

此时,模板引擎注意到 base.html 中的三个 {% block %} 标签,并用子模板的内容替换这些 block 。因此,引擎将会使用我们在 { block title %} 中定义的标题,对 {% block content %} 也是如此。 所以,网页标题一块将由 {% block title %}替换,同样地,网页的内容一块将由 {% block content %}替换。

注意由于子模板并没有定义 footer 块,模板系统将使用在父模板中定义的值。 父模板 {% block %} 标签中的内容总是被当作一条退路。

继承并不会影响到模板的上下文。 换句话说,任何处在继承树上的模板都可以访问到你传到模板中的每一个模板变量。

你可以根据需要使用任意多的继承次数。 使用继承的一种常见方式是下面的三层法:

  •     创建 base.html 模板,在其中定义站点的主要外观感受。 这些都是不常修改甚至从不修改的部分。
  •     为网站的每个区域创建 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。这些模板对 base.html 进行拓展,并包含区域特定的风格与设计。
  •     为每种类型的页面创建独立的模板,例如论坛页面或者图片库。 这些模板拓展相应的区域模板。

这个方法可最大限度地重用代码,并使得向公共区域(如区域级的导航)添加内容成为一件轻松的工作。

以下是使用模板继承的一些诀窍:

  •     如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。
  •     一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越多越好。
  •     如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。
  •     如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
  •     不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。 也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。
  •     {% extends %} 对所传入模板名称使用的加载方法和 get_template() 相同。 也就是说,会将模板名称被添加到 TEMPLATE_DIRS 设置之后。
  •     多数情况下, {% extends %} 的参数应该是字符串,但是如果直到运行时方能确定父模板名,这个参数也可以是个变量。 这使得你能够实现一些很酷的动态功能。
Python 相关文章推荐
Python 错误和异常小结
Oct 09 Python
python批量提交沙箱问题实例
Oct 08 Python
Pyhton中防止SQL注入的方法
Feb 05 Python
利用Python为iOS10生成图标和截屏
Sep 24 Python
python清理子进程机制剖析
Nov 23 Python
python编写计算器功能
Oct 25 Python
关于Numpy中的行向量和列向量详解
Nov 30 Python
python爬虫添加请求头代码实例
Dec 28 Python
什么是python的自省
Jun 21 Python
python文件操作seek()偏移量,读取指正到指定位置操作
Jul 05 Python
使用jupyter notebook运行python和R的步骤
Aug 13 Python
python eventlet绿化和patch原理
Nov 21 Python
Django中模版的子目录与include标签的使用方法
Jul 16 #Python
Django中使用locals()函数的技巧
Jul 16 #Python
Django框架中render_to_response()函数的使用方法
Jul 16 #Python
在Python的Django框架中加载模版的方法
Jul 16 #Python
Django框架下在视图中使用模版的方法
Jul 16 #Python
详解Django中的过滤器
Jul 16 #Python
在Python的Django框架中为代码添加注释的方法
Jul 16 #Python
You might like
一周学会PHP(视频)Http下载
2006/12/12 PHP
php 判断数组是几维数组
2013/03/20 PHP
解析Win7 XAMPP apache无法启动的问题
2013/06/26 PHP
10个实用的PHP正则表达式汇总
2014/10/23 PHP
php从csv文件读取数据并输出到网页的方法
2015/03/14 PHP
php函数传值的引用传递注意事项分析
2016/06/25 PHP
Thinkphp5.0框架视图view的模板布局用法分析
2019/10/12 PHP
javascript mouseover、mouseout停止事件冒泡的解决方案
2009/04/07 Javascript
PHP+mysql+Highcharts生成饼状图
2015/05/04 Javascript
javascript常见数据验证插件大全
2015/08/03 Javascript
快速掌握Node.js模块封装及使用
2016/03/21 Javascript
原生javascript实现的一个简单动画效果
2016/03/30 Javascript
原生JS封装Ajax插件(同域、jsonp跨域)
2016/05/03 Javascript
JavaScript_ECMA5数组新特性详解
2016/06/12 Javascript
基于原生js淡入淡出函数封装(兼容IE)
2016/10/20 Javascript
谈谈JS中常遇到的浏览器兼容问题和解决方法
2016/12/17 Javascript
Bootstrap table右键功能实现方法
2017/02/20 Javascript
推荐三款日期选择插件(My97DatePicker、jquery.datepicker、Mobiscroll)
2017/04/21 jQuery
详解Vue中localstorage和sessionstorage的使用
2017/12/22 Javascript
axios全局请求参数设置,请求及返回拦截器的方法
2018/03/05 Javascript
react中hook介绍以及使用教程
2020/12/11 Javascript
[03:30]完美盛典趣味短片 CSGO2019年度名场面
2019/12/07 DOTA
pycharm中连接mysql数据库的步骤详解
2017/05/02 Python
Python之列表实现栈的工作功能
2019/01/28 Python
Pandas DataFrame中的tuple元素遍历的实现
2019/10/23 Python
python 在右键菜单中加入复制目标文件的有效存放路径(单斜杠或者双反斜杠)
2020/04/08 Python
Python 找出出现次数超过数组长度一半的元素实例
2020/05/11 Python
Lentiamo荷兰:在线订购隐形眼镜、隐形眼镜液和太阳镜
2019/10/25 全球购物
给排水专业应届生求职信
2013/10/12 职场文书
人力资源专员岗位职责
2014/01/30 职场文书
群众路线教育实践活动方案
2014/02/02 职场文书
五星级酒店餐饮部总监的标准岗位职责
2014/02/17 职场文书
企业党员公开承诺书
2014/03/26 职场文书
赡养老人协议书
2014/04/21 职场文书
司法局群众路线教育实践活动整改措施思想汇报
2014/10/13 职场文书
教师正风肃纪剖析材料
2014/10/20 职场文书