学习Tango with django(8)

使用模板

目前为止,我们已经为rango应用的不同页面创建了几个HTML模板。当你创建了越来越多的模板时,你就会发现许多HTML模板代码都是重复的。这就违反了DIY原则,还有你也许注意到我们在不同页面引用URL路径时使用了硬编码。综上,维护网址将变成恶梦,因为如果我们想修改一个一般的网址结构或者一个URL路径,我们将要逐个修改每个模板。
这个章节中,我们使用模板继承来解决第一个问题,使用模板的URL标签解决第二个问题。我们先开始解决第二个问题。

在模板中使用相对URL

目前为止,我们都是直接将页面或视图的URL硬编码到我们的模板里,即<a href='/rango/about'>About</a>。这样的URL硬编码导致如果我们在urls.py文件里修改了URL映射,我们就得修改这个URL所有的引用。更好的方式是使用模板标签url,查找urls.py文件里的url,动态插入url路径。
在模板中包括一个相对URL是相当简单的一件事。比如你要引用about页面,我们就把下面这一行插入到模板中。

<a href="{% url 'about' %}">About</a>

django模板引擎将会从所有的urls.py文件中寻找带有属性name并且值为about(name=’about’)的url模式,然后反推出匹配的实际URL。如果我们在urls.py中修改了URL映射,我们就不用在模板中一个一个的修改了。
还有一种方法不用指定name也可以引用URL模式,如下所示,可以直接引用视图。

<a href="{% url 'rango.views.about' %}">About</a>

在这个例子里,我们必须要保证应用rango的views.py文件里一定要有about视图。
在应用的index.html模板里,你将注意到你有一个参数化的URL模式(show_category视图和URL都将category.slug作为参数),你可以将URL或视图的name和slug传递给模板标签url,如下所示:

{% for category in categories %}
    <li>
        <a href="{% url 'show_category' category.slug %]">
            {{ category.name }}
        </a>
    </li>
{% endfor %}

运行后就能使用相对URL更新所有模板的URL了,但是在运行前,让我们通过模板继承重建我们的模板,删除冗余的代码。

url和django多应用
这本书仅关注于单个应用开发。但是你也会发现一个django项目是会利用多个应用的。所以你可能就得面对成百个潜在的URL需要引用的问题。这种会产生问题,我们该如何组织我们的URL?两个app中有可能会有命名重复的url,这就会产生潜在的冲突。
django提供了命名空间(namespace)的方法,为项目里的每一个应用配置一个命名空间。只需要在应用的urls.py文件里增加一个app_name的变量就够了。下面的例子为rango的应用指定了命名空间,并命名为rango。

from django.conf.urls import url
from rango import views

app_name = 'rango'
urlpatterns =[
url(r'^$', views.index, name='index'),
]

增加了app_name的变量后,从rango应用中引用URL就需要这样做:

<a href="{% url 'rango:about' %}">About</a>

url命令中的冒号将url名称从名称空间分开。当然这是一个多应用使用的高级属性。但是当项目规模越来越大时,这是一个有用的诀窍。

处理重复代码

现在几乎每一个专业的网站设计都有一系列重复的组件(比如页头,页边,页脚),在这些重复的组件里重复着相同的代码是非常不明智的。如果你要修改网页的页头该怎么办?你需要检查每个页面并修改其中的页头。这会消耗大量时间,甚至还有可能产生人为错误。
为了避免复制粘贴HTML代码而浪费大量的时间,我们可以通过django模板语言提供的模板继承最大程度降低rango应用的代码重复。
在模板中使用继承的方法如下:

  1. 检查应用中每个页面重用的部分(即页头、页边、页脚、正文)。有时候,在纸上画出不同页面的基本结构能够帮助你找到哪些组件是重用的。
  2. 在base模板中,提供了一个基本页面的框架结构,以及一些共同的内容(即页脚的版权信息,某些区域出现的logo和标题)。然后根据用户要查看的页面内容来定义数个要修改的块。
  3. 为应用的页面创建具体的模板。所有的模板都继承于基类模板,每个块都指定了内容。

重用HTML和基类模板

目前为止我们已经创建了数个模板,很明显这些模板里有相当一部分的代码是重复的。下面的代码中,我们已经抽象出页面的细节,只显示在每个模板中重复组件的框架结构。

<!DOCTYPE html>
{% load staticfiles %}
<html>
    <head lang="en">
        <meta charset="UTF-8" />
        <title>Rango</title>
    </head>
    <body>
        <!--Page spedfic content goes here -->
    </body>
</html>

我们暂时把这个实例作为我们应用的基类模板。保存上面的代码并命名为base.html,存放到templates/rango/目录中(即templates/rango/base.html)。

注意DOCTYPE的使用
记住<!DOCTYPE html>的声明一定要放在模板的第一行,如果第一行没有关于文档类型的声明,那么冲模板中生成的结果页面就不会执行W3C的html方针。

模板块

既然我们已经创建好了基类模板,我们就可以添加模板标签用来表示模板的哪些部分可以通过模板继承进行重写。要实现这样的功能我们需要使用block标签。举个例子,如下所示,我们可以在base.html中添加一个块body_block:

<!DOCTYPE html>
{% load staticfiles %}
<html>
    <head lang="en">
        <meta charset="UTF-8" />
        <title>Rango</title>
    </head>
    <body>
        {% block body_block %}
        {% endblock %}
    </body>
</html>

回顾下django模板的标准命令都是通过{%%}表示的。block命令表示为{% block <NAME> %},此处的NAME就是你想创建的block的名称。你要确保block要以{% endblock %}结束,要用django的模板标签包围。
你也可以为block指定默认内容,这个默认内容将在继承模板中没有定义此block时使用。指定默认内容就是在{% block %}{% endblock %}之间添加你想要展现的默认内容就可以了,如下所示:

{% block body_block %}
    This is body_block's default content.
{% endblock %}

当我们为每个页面创建模板时,我们需要继承rango/base.html,然后重写块body_block里的内容。其实你可以根据你自己需求在模板中随意添加block块。举个例子,你可以创建一个标题块,一个页脚块,一个页边块,甚至更多。block是django模板系统中非常重要的属性,你可以查阅django官方文档获取更多信息。

抽取共同结构
你应该尽可能的将页面重用部分放置在基类模板中,尽管这样做会很麻烦,但是你后期维护所节省的时间会远远超过你思考页面重用时所花费的时间。

Thinking hurts, but it is better than doing lots of grunt work!
思想会很累,但总比做大量的繁重工作要好!

进一步抽象

既然你已经理解了django模板的block块,让我们抓住机会进一步抽象我们的基类模板。重新打开rango/base.html模板,按照如下模板进行修改:

<!DOCTYPE html>
{% 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模板里导入了一个链接列表,并把它们放在了body_block块下面的HTML标签div里。这就保证了从基类模板中继承的页面都会存在这些链接。这些链接前有一个分割线(<hr />),让用户在body_block块和链接之间能看到一条分割线。

模板继承

既然我们已经创建好了含有块的模板,现在我们就来修改之前创建的模板,让它们都从基类模板中继承。让我们首先开始重构rango/category.html模板。
首先删除所有重复的HTML代码,只留下页面中具体的HTML和模板标签或命令。然后在模板的最开始添加如下面的一行:

{% extends 'rango/base.html' %}

extends命令需要有一个参数,这个参数表示所要继承页面模板的名称(即rango/base.html)。在extends命令中提供的继承模板参数应该是相对于项目的templates目录的。比如说,rango应用中使用的所有的模板都应该继承于rango/base.html,而不是base.html。我们继续修改category.html模板让它看起来如下面的完整示例。

{% extends 'rango/base.html' %}
{% load staticfiles %}

{% block title_block %}
    {{ category.name }}
{% endblock %}

{% block body_block %}
    {% if category %}
        <h1>{{ category.name }}</hl>

        (% if pages %}
            <ul>
            {% for page in pages %}
                <li><a href="{{ page.url }}">{{ page.title }}</a></li>
            {% endfor %}
            </ui>
        {% 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 %}用来加载静态媒体文件。如果不这样做,就会出错。必须要为每个需要处理静态文件的模板各自导入djano模板的模块。如果你之前有过编程经验,这样的处置方法似乎与如Java之类的面向对象编程语言稍有不同,java会导入级联继承类。注意我们是如何使用模板标签url来引用rango/<category-name>/add_page/的URL模式的。category.slug作为参数传递给url模板标签,django模板引擎会自动为我们生成正确的URL。

我们已经继承了rango/base.html基类模板,现在看起来category.html模板简洁多了,它只是扩展了title_block和body_block两个块。你并不需要一个格式良好的HTML文件,因为base.html提供了所有的格式基础。你要做的就是往基类模板中插入额外的内容创建新的模板,渲染模板并发送到客户端服务器。经过渲染后的HTML文件将会符合标准,包含基本组件,如第一行的文件类型声明。

关于模板的更多信息
这里我们演示了如何在模板中减少HTML的重复。然而django模板语言是非常强大的,它甚至能够让你自定义模板标签。
模板也可以用来减少应用的视图代码。比如说,如果你想为每个页面导入相同的数据库引擎,你可以构建一个模板调用指定的视图函数来处理页面中的重复部分。这样就节省了在视图里调用django的ORM函数获取数据对模板进行渲染的时间。
如果你还没有看过django的官方文档,你现在可以看看了。

练习
既然你已经完成了这一章节的学习,接下来你需要做几个练习来加强一下关于django模板的相关知识。

  • 修改之前rango应用中定义的所有模板,让它们从基类模板base.html继承,就跟上面演示的过程一样。修改完后你的模板应该都继承base.html模板。
  • 现在已经用模板继承了,你应该把index.html里的链接删除掉,我们不再需要了。你还要把about.html模板里的rango主页链接删除掉。
  • 重构index.html模板时,要保留静态媒体文件服务器提供的图像。
  • 使用模板标签url更新所有rango的URL引用。在views.py里也要这样做,使用reverse()函数实现目的。

    提示

  • 首先重构about.html模板。

  • 在每个模板中修改title_block和body_block块。
  • 运行开发服务器检查页面是否正确显示。不要一下修改所有的页面,这样出现问题时不好定位。一个一个的修改,然后测试修改是相当安全的方法。
  • 要引用category页面链接,可以参照下面的代码,要特别注意django模板{% url %}的使用。
<a href="{% url 'show_category' category.slug %}">{{ category.name }}</a>

##render()方法和request上下文
编写视图代码时我们已经尝试了好几个不同的渲染方法,一个更好的方法就是使用django的快捷方法render()。rener()方法需要你传递request作为第一个参数。request上下文存放了许多与会话、登录用户等相关的信息,查看django官方文档获取更多信息。将request传递给模板表示在创建模板时你还可以访问那些信息。下个章节,我们将会访问关于user的信息。现在我们需要检查一下所有的视图函数,确保他们用render()方法实现,要不然你的模板就获取不到会话等相关信息。

渲染和上下文
about视图是一个你必须要执行检查的简单样例。如下所示,最开始是通过硬编码返回字符串实现的。这里我们仅发送字符串,我们并没有用到request参数。

def about(request):
    return HttpResponse('
        Rango says: Here is the about page.
        <a href="/rango">Index</a>')

为了展示模板的使用方法,我们调用了render()函数并传递了request对象。这样就能让你的模板引擎访问到request请求类型的数据(例如GET/POST)和一些与用户状态相关的信息(查看第9章)。

def about(request):
    #   prints out whether the method is a GET or a POST
    print(request.method)
    #   prints out the user name, if no one is logged in it prints 'AnonymousUser' 
    print(request.user)
    return render(request, 'rango/about.html', {})

记住,render()函数的最后一个参数就是上下文字典,你可以利用它来传递额外的数据给django的模板引擎。这里我们不需要传递额外数据,我们就传递来一个空的字典{}。

定制模板标签

让用户通过每个页面的工具栏能够访问到不同的分类是个不错的用户体验。结合目前学到的知识,我们可以做到如下地步:

  • 在base.html模板里,我们可以增加代码用来展示分类列表,
  • 在每个视图里,我们可以访问所有的Category对象,获取所有的分类,并通过上下文字典返回。

然而,这是一个非常差的方法,因为我们需要在所有的视图里不断地重复着相同的代码。一个DIY式的解决方案就是在模板里导入一个自定义一个标签,这个标签可以自动请求数据。

使用模板标签

创建一个目录rango/templatetags,在里面再创建两个模块。一个模块必须叫__init__.py,这个模块保留空白;另一个模块必须叫rango_template_tag.py,在这个文件里添加如下代码。

from django import template
from rango.models import Category

register = template.Library()

@register.inclusion_tag('rango/cats.htm1')
def get_category_list():
    return {’cats': Category.objects.al1()}

上面的代码片段中,你看到了一个新的叫get_category_list的方法,这个方法返回了分类的列表,但是是同rango/cats.html模板混合在一起了(可以在register.inclusion_tag装饰器中看出)。现在你可以创建一个模板文件,增加如下的HTML代码。

<ul>
{% if cats %}
    {% for c in cats %}
    <li><a href="{% url 'show_category' c.slug %}">{{ c.name }}</a></li>
    {% endfor %}
{% else %}
    <li> <strong>There are no categories present.</strong></li>
{% endif %}
</ul>

要在基类模板中使用自定义模板标签,首先要在base.html模板上方导入命令{% load rango_template_tags %} 来加载自定义标签。然后你就可以创建一个新的块用来表示工具栏,这样我们就可以调用我们自定义的新标签了。

<div>
    {% block sidebar_block %}
        {% get_category_list %}
    {% endblock %}
</div>

试试看,现在所有从base.html继承的页面都会包括分类列表(之后我们会移到工具栏上)。

重启服务器
每次修改了模板标签后,你需要重启django开发服务器(或确保它自动重启)。如果服务器不重启,django就不会注册标签。

参数化模板标签

我们也可以让自定义的模板标签带上参数,非常灵活。例如,我们用参数实现高亮显示用户正在访问的那个分类。添加一个参数是很容易的,如下修改get_category_list()方法:

def get_category_list(cat=None):
    return {'cats': Category.objects.al1(),
        'act_cat': cat}

注意get_category_list()里的cat参数是可选的,如果你不传递一个分类进去,那默认值就是None。
然后我们修改base.html模板,利用自定义的标签传递当前的分类(只有它存在才能传递)。

<div>
    {% block sidebar_block %}
        {% get_category_list category %}
    {% endblock %}
</div>

然后再更新cat.html模板。

{% for c in cats %}
    {% if c == act_cat %}
        <li>
        <strong>
            <a href="{% url 'show_category' c.slug %}">{{ c.name }}</a>
        </strong>
        </li>
    {% else %}
        <li>
            <a href="{% url 'show_category' c.slug %}">{{ c.name }}</a>
        </li>
    {% endif %}
{% endfor %}

在模板中我们通过for循环来检查正在展示的分类是否同传递进来的分类一样(即c == act_cat)。如果是,我们就使用<strong>标签加粗文本以突出分类名称。

总结

这个章节中,我们展示了我们是如何:

  • 通过使用模板标签url生成相对URL来降低URL和模板之间的耦合;
  • 使用模板继承减少了样板代码数量;
  • 使用自定义标签避免了视图函数里重复代码的出现。

综上,你的模板代码现在应该更简洁,更容易维护了。当然django模板还提供了许多其它的功能,访问官方文档获取更多信息。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值