到目前为止,我们已经在我们的rango应用内创造了几个HTML模板对于不同的页面。随着你创造越来越多的模板,你也许注意到许多HTML代码实际上是重复的。
我们拥护DRY原则
。而且,你可能注意到,我们使用硬编码的URL路径来引用不同的网页。 总的来说,维护网站将是噩梦,因为如果我们要更改一般网站结构或更改URL路径,我们将必须修改每个模板。
在本章中,我们将使用模板继承来克服第一个问题,并使用URL模板标签来解决第二个问题。 我们将首先解决后一个问题。
在模板中使用相对URL
到目前为止,我们一直在直接编码我们想在模板中显示的网页或视图的网址,即<a href="/rango/about/">About</a>
。这种类型的URL的硬编码意味着如果我们更改URL映射urls.py,那么我们还必须更改所有这些URL引用。首选方法是使用模板标签url查找urls.py文件中的URL并动态插入URL路径。
在模板中包含相对网址很简单。要引用About
页面,我们将在模板中插入以下行:
{lang =“html”,linenos = off} About
The Django template engine will look up any urls.py module for a URL pattern with the attribute name set to about (name=’about’), and then reverse match the actual URL. This means if we change the URL mappings in urls.py, we don’t have to go through all our templates and update them.
One can also reference a URL pattern without a specified name, by referencing the view directly as shown below.
{lang="html",linenos=off} About
In this example, we must ensure that the app rango has the view about, contained within its views.py module.
In your app’s index.html template, you will notice that you have a parameterised URL pattern (the show_category URL/view takes the category.slug as a parameter). To handle this, you can pass the url template tag the name of the URL/view and the slug within the template, as follows:
{lang="html",linenos=off} {% for category in categories %}
{{ category.name }}
{% endfor %}
Before you run off to update all the URLs in all your templates with relative URLs, we need to restructure and refactor our templates by using inheritance to remove repetition.
处理重复
虽然几乎所有的专业网站,你使用将有一系列重复的组件(例如页眉,侧边栏和页脚,例如),重复HTML的每个这些重复组件不是一个特别明智的方式来处理这个。如果您想更改网站标头的一部分怎么办?您需要浏览每个页面并更改标题的每个副本以适应。这可能需要很长时间 - 并允许人为错误的可能性蠕变。
而不是花费(或浪费!)大量的时间复制和粘贴您的HTML标记,我们可以通过使用Django的模板语言提供的模板继承,最小化Rango的代码库中的重复。
在模板中使用继承的基本方法如下。
- 标识每个页面在应用程序中重复出现的部分(即标题栏,侧边栏,页脚,内容窗格)。有时,它可以帮助您在纸上绘制不同页面的基本结构,以帮助您发现哪些组件是常见的。
- 在基本模板中,提供基本页面的骨架结构以及任何常见内容(即页脚中包含的版权声明,部分中显示的徽标和标题)。然后,根据用户正在查看的页面,定义可能更改的多个块。
- 为应用程序的页面创建特定的模板 - 所有这些模板都继承自基本模板,并指定每个块的内容。
重复的HTML和基本模板
给定我们迄今为止创建的模板,应该很明显,我们已经重复了一个公平的HTML代码。下面,我们抽象了任何页面的具体细节,以显示我们在每个模板中重复的骨架结构。
{lang =“html”,linenos = on} {%load staticfiles%}
<html>
<head lang="en">
<meta charset="UTF-8" />
<title>Rango</title>
</head>
<body>
<!-- Page specific content goes here -->
</body>
</html>
Template Blocks
现在我们已经创建了基本模板,我们可以添加模板标签来表示模板的哪些部分可以被继承的模板覆盖。为此,我们将使用block
标记。例如,我们可以将body_block
基础模板添加到base.html
如下:
{lang =“html”,linenos = on} {%load staticfiles%}
<html>
<head lang="en">
<meta charset="UTF-8" />
<title>Rango</title>
</head>
<body>
{% block body_block %}
{% endblock %}
</body>
</html>
回想一下,标准的Django模板命令由{%
和%}
标签表示。要启动一个块,命令是{% block <NAME> %}
,其中<NAME>
是您要创建的块的名称。您还必须确保使用{% endblock %}
命令关闭块,该命令再次包含在Django模板标记中。
您还可以指定默认内容为你的块,将用于如果没有继承模板定义给定块(见进一步下跌)。通过在{% block %}
和{% endblock %}
模板命令之间添加HTML标记,可以轻松实现指定默认内容,就像下面的示例一样。
{lang =“html”,linenos = off}
{%block body_block%}
这是body_block的默认内容。
{%endblock%}
当我们为每个页面创建模板时,我们将继承rango/base.html并覆盖的内容body_block。但是,您可以在模板中放置尽可能多的块。例如,您可以为页面标题创建一个块,为页脚创建一个块,为侧边栏创建一个块等。块是Django的模板系统的一个非常强大的功能,你可以了解更多关于他们检查Django官方文档的模板。
抽象进一步
现在您已经了解了Django模板中的块,让我们借此机会抽象一些基本模板。重新打开rango/base.html模板并将其修改为如下所示。
{lang =“html”,linenos = on} {%load staticfiles%}
<html>
<head>
<title>
Rango -
{% block title_block %}
How to Tango with Django!
{% endblock %}
</title>
</head>
<body>
<div>
{% block body_block %}
{% endblock %}
</div>
<hr />
<div>
<ul>
<li><a href="{% url 'add_category' %}">Add New Category</a></li>
<li><a href="{% url 'about' %}">About</a></li>
<li><a href="{% url 'index' %}">Index</a></li>
</ul>
</div>
</body>
</html>
从上面的例子,我们已经在基本模板中引入了两个新特性。
- 第一个是调用的模板块title_block。这将允许我们为从我们的基本模板继承的每个页面指定自定义页面标题。如果继承页面不覆盖阻止,则title_block默认为How to Tango with Django!,导致完整的标题Rango - How to Tango with Django!。看看
<title>
上面模板中的标签的内容,看看它是如何工作的。 - 我们还包括来自当前index.html模板的链接列表,并将它们放置在
<div>
我们body_block
块下面的HTML标记中。这将确保链接存在于从基本模板继承的所有页面上。链接之前是水平规则(<hr />)
,其在body_block块的内容和链接之间为用户提供视觉分离。
模板继承{#section-templates-inheritance}
现在我们已经创建了一个带有块的基本模板,现在我们可以更新所有已创建的模板,以便它们从基本模板继承。让我们从重构模板开始rango/category.html
。
要这样做,首先删除所有重复的HTML代码,只留下特定于页面的HTML和模板标记/命令。然后在模板的开头添加以下代码行:
{lang =“html”,linenos = off} {%extends’rango / base.html’%}
该extends
命令采用一个参数 - 要从(ie rango/base.html)扩展/继承的模板。您提供给extends命令的参数应该与项目的templates目录相对。例如,我们用于Rango的所有模板都应该从rango/base.html,而不是base.html。然后我们可以进一步修改category.html模板,使其看起来像下面的完整示例。
{lang =“html”,linenos = on} {%extends’rango / base.html’%} {%load
staticfiles%}
{% block title_block %}
{{ category.name }}
{% endblock %}
{% block body_block %}
{% if category %}
<h1>{{ category.name }}</h1>
{% if pages %}
<ul>
{% for page in pages %}
<li><a href="{{ page.url }}">{{ page.title }}</a></li>
{% endfor %}
</ul>
{% else %}
<strong>No pages currently in category.</strong>
{% endif %}
<a href="{% url 'add_page' category.slug %}">Add a Page</a>
{% else %}
The specified category does not exist!
{% endif %}
{% endblock %}
您需要确保添加{% load staticfiles %}到使用静态媒体的每个模板的顶部。如果你不这样做,你会得到一个错误!必须为需要它们的每个模板单独导入Django模板模块。
现在我们继承了rango/base.html
,category.html
模板是更清洁扩展title_block
和body_block
块。您不需要格式良好的HTML文档,因为base.html它为您提供了所有基础工作。您所做的就是将额外的内容插入基本模板,以创建发送到客户端浏览器的完整的呈现的HTML文档。这个呈现的HTML文档将符合标准,包含第一行上的文档类型声明等组件。
这里我们展示了如何在模板中最小化结构HTML的重复。
但是,Django模板语言非常强大,甚至允许您创建自己的模板标记。模板也可以用来最小化应用程序视图中的代码。例如,如果您想在应用程序的每个页面上包含相同的数据库驱动内容,则可以构建一个模板,该模板调用特定视图来处理应用程序页面的重复部分。这样就可以避免您必须调用Django ORM函数,该函数在渲染模板的每个视图中收集模板所需的数据。
Exercises
现在你已经完成了本章,你可以通过一些练习来强化你对Django和模板的了解。
更新所有其他以前定义的模板在Rango应用程序从新base.html模板扩展。按照我们上面演示的相同的过程。一旦完成,您的模板应该都继承base.html。
当你在它,确保你从我们的index.html模板中删除的链接。我们不再需要他们了!您也可以在about.html模板中删除Rango主页的链接。
当您重构index.html保留从静态文件和媒体服务器提供的图像
使用url模板标记更新所有对Rango网址的引用。views.pyreverse()
Hints
- 首先开始重构about.html模板。
- 更新
title_block
然后body_block
在每个模板中。 - 让开发服务器运行并检查页面,当你在上面工作。不要改变整个页面,发现它不工作。逐步改变事物并在你走时测试这些变化是一个更安全的解决方案。
- 要引用类别页面的链接,您可以使用以下模板代码,特别注意Django模板{% url %}命令。
{lang =“html”,linenos = off} T> {{category.name}}
The render() Method and the request Context
当编写视图时,我们使用了许多不同的方法,首选的方法是使用Django快捷方法render()。该render()方法要求您通过request作为第一个参数。该request范围内安置了许多关于会话信息,用户等,看到的请求对象官方Django文档。通过将通过传递request到模板意味着,您还将有权访问这些信息时创建模板。在下一章中,我们将访问有关的信息user- 但现在检查所有视图,并确保它们已使用该render()方法实现。否则,您的模板将不会有我们以后需要的信息。
自定义模板标签
这将是很好,显示不同的类别,用户可以浏览在每个页面上的侧边栏。鉴于我们迄今为止所学到的知识,我们可以做到以下几点:
- 在base.html模板中,我们可以添加一些代码来显示类别的项目列表
- 在每个视图中,我们可以访问Category对象,获取所有类别,并在上下文字典中返回
然而,这是一个非常讨厌的解决方案,因为我们需要在所有视图中重复包含相同的代码。一个烘干机的解决方案是创建包括在模板定制模板标记,并且可以要求他们自己的数据。
使用模板标签
创建一个目录rango/templatetags,并在其中创建两个新模块。必须调用一个__init__.py
,这将保持空白。命名第二个模块rango_template_tags.py
。将以下代码添加到此第二个模块。
from django import template
from rango.models import Category
register = template.Library()
@register.inclusion_tag('rango/cats.html')
def get_category_list():
return {'cats':Category.objects.all()}
从此代码段,您可以看到一个新的方法调用get_category_list()
。此方法返回类别的列表,但是与模板混合rango/cats.html
(从register.inclusion_tag()
装饰器可以看出)。您现在可以创建此模板文件,并添加以下HTML标记:
要在模板中使用模板标记,请base.html首先通过{% load rango_template_tags %}
在base.html模板顶部包含命令来加载自定义模板标记。然后,您可以创建一个新块来表示边栏 - 我们可以使用以下代码调用我们的新模板标签。
{%block sidebar_block%}
{%get_category_list%}
{%endblock%}
试试看。现在,所有继承的页面都base.html将包括类别列表(稍后我们将移动到这一侧)。
在模板中,我们检查显示的类别是否与在for循环(即c == act_cat)期间传递的类别相同。如果是,我们通过使用标记使其粗体突出显示类别名称<strong>
。