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

在实际应用中,你将用 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 %} 的参数应该是字符串,但是如果直到运行时方能确定父模板名,这个参数也可以是个变量。 这使得你能够实现一些很酷的动态功能。
(0)

相关推荐

  • 在Python的Django框架中加载模版的方法

    为了减少模板加载调用过程及模板本身的冗余代码,Django 提供了一种使用方便且功能强大的 API ,用于从磁盘中加载模板, 要使用此模板加载API,首先你必须将模板的保存位置告诉框架. 设置的保存文件就是settings.py. 如果你是一步步跟随我们学习过来的,马上打开你的settings.py配置文件,找到TEMPLATE_DIRS这项设置吧. 它的默认设置是一个空元组(tuple),加上一些自动生成的注释. TEMPLATE_DIRS = ( # Put strings here, li

  • Django中模版的子目录与include标签的使用方法

    get_template()中使用子目录 把所有的模板都存放在一个目录下可能会让事情变得难以掌控. 你可能会考虑把模板存放在你模板目录的子目录中,这非常好. 事实上,我们推荐这样做:一些Django的高级特性(例如将在第十一章讲到的通用视图系统)的缺省约定就是期望使用这种模板布局. 把模板存放于模板目录的子目录中是件很轻松的事情. 只需在调用 get_template() 时,把子目录名和一条斜杠添加到模板名称之前,如: t = get_template('dateapp/current_dat

  • Django框架下在视图中使用模版的方法

    打开current_datetime 视图. 以下是其内容: from django.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(h

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

    在实际应用中,你将用 Django 模板系统来创建整个 HTML 页面. 这就带来一个常见的 Web 开发问题: 在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代码? 解决该问题的传统做法是使用 服务器端的 includes ,你可以在 HTML 页面中使用该指令将一个网页嵌入到另一个中. 事实上, Django 通过刚才讲述的 {% include %} 支持了这种方法. 但是用 Django 解决此类问题的首选方法是使用更加优雅的策略-- 模板继承 . 本质上来说,模板

  • 详解Python的Django框架中的模版相关知识

    HTML被直接硬编码在 Python 代码之中. def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html) 尽管这种技术便于解释视图是如何工作的,但直接将HTML硬编码到你的视图里却并不是一个好主意. 让我们来看一下

  • 详解Python的Django框架中manage命令的使用与扩展

    [简介] django-admin.py是Django的一个用于管理任务的命令行工具.本文将描述它的大概用法. 另外,在每一个Django project中都会有一个manage.py.manage.py是对django-admin.py的简单包装,它额外帮助我们做了两件事情: 它将你的project的包放到sys.path中 它将DJANGO_SETTINGS_MODULE环境变量设置为了你的project的setting.py文件的位置. 如果你是通过setup.py工具来安装Django的

  • 详解Python的Django框架中的templates设置

    TEMPLATES Django 1.8的新特性 一个列表,包含所有在Django中使用的模板引擎的设置.列表中的每一项都是一个字典,包含某个引擎的选项. 以下是一个简单的设定,告诉Django模板引擎从已安装的应用程序(installed applications)的templates子目录中读取模板: TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True,

  • 详解Python的Django框架中Manager方法的使用

    在语句Book.objects.all()中,objects是一个特殊的属性,需要通过它查询数据库. 在第5章,我们只是简要地说这是模块的manager .现在是时候深入了解managers是什么和如何使用了. 总之,模块manager是一个对象,Django模块通过它进行数据库查询. 每个Django模块至少有一个manager,你可以创建自定义manager以定制数据库访问. 下面是你创建自定义manager的两个原因: 增加额外的manager方法,和/或修manager返回的初始Quer

  • 详解Python的Django框架中inclusion_tag的使用

    另外一类常用的模板标签是通过渲染 其他 模板显示数据的. 比如说,Django的后台管理界面,它使用了自定义的模板标签来显示新增/编辑表单页面下部的按钮. 那些按钮看起来总是一样的,但是链接却随着所编辑的对象的不同而改变. 这就是一个使用小模板很好的例子,这些小模板就是当前对象的详细信息. 这些排序标签被称为 包含标签 .如何写包含标签最好通过举例来说明. 让我们来写一个能够产生指定作者对象的书籍清单的标签. 我们将这样利用标签: {% books_for_author author %} 结果

  • 详解Python的Django框架中的中间件

    什么是中间件 我们从一个简单的例子开始. 高流量的站点通常需要将Django部署在负载平衡proxy之后. 这种方式将带来一些复杂性,其一就是每个request中的远程IP地址(request.META["REMOTE_IP"])将指向该负载平衡proxy,而不是发起这个request的实际IP. 负载平衡proxy处理这个问题的方法在特殊的 X-Forwarded-For 中设置实际发起请求的IP. 因此,需要一个小小的中间件来确保运行在proxy之后的站点也能够在 request.

  • 详解Python的Django框架中的通用视图

    通用视图 1. 前言 回想一下,在Django中view层起到的作用是相当于controller的角色,在view中实施的 动作,一般是取得请求参数,再从model中得到数据,再通过数据创建模板,返回相应 响应对象.但在一些比较通用的功能中,比如显示对象列表,显示某对象信息,如果反复 写这么多流程的代码,也是一件浪费时间的事,在这里,Django同样给我们提供了类似的 "shortcut"捷径--通用视图. 2. 使用通用视图 使用通用视图的方法就是在urls.py这个路径配置文件中进

  • 详解Python的Django框架中的Cookie相关处理

    浏览器的开发者在很早的时候就已经意识到, HTTP's 的无状态会对Web开发者带来很大的问题,于是(cookies)应运而生. cookies 是浏览器为 Web 服务器存储的一小段信息. 每次浏览器从某个服务器请求页面时,它向服务器回送之前收到的cookies 来看看它是怎么工作的. 当你打开浏览器并访问 google.com ,你的浏览器会给Google发送一个HTTP请求,起始部分就象这样: GET / HTTP/1.1 Host: google.com ... 当 Google响应时,

  • 详解Python的Twisted框架中reactor事件管理器的用法

    铺垫 在大量的实践中,似乎我们总是通过类似的方式来使用异步编程: 监听事件 事件发生执行对应的回调函数 回调完成(可能产生新的事件添加进监听队列) 回到1,监听事件 因此我们将这样的异步模式称为Reactor模式,例如在iOS开发中的Run Loop概念,实际上非常类似于Reactor loop,主线程的Run Loop监听屏幕UI事件,一旦发生UI事件则执行对应的事件处理代码,还可以通过GCD等方式产生事件至主线程执行. 上图是boost对Reactor模式的描绘,Twisted的设计就是基于

随机推荐