Django第四篇-----模板后半篇

目录

基本的标签和过滤器

 Django Template Language(DTL)设计理念

在视图中使用模板

模板加载机制

 模板目录

render()

模板子目录

 include 模板标签

模板继承

使用模板继承的一些指导方针:


基本的标签和过滤器

模板过滤器是在显示变量之前调整变量值的简单方式。过滤器使用管道符号指定,如下所示

{{ name|lower }}

上述代码先通过 lower 过滤器调整 {{ name }} 变量的值——把文本转换成小写,然后再显示。过滤器可以串接,即把一个过滤器的输出传给下一个过滤器。

下述示例获取列表中的第一个元素,然后将其转换成大写:

{{ my_list|first|upper }}

有些过滤器可以接受参数。过滤器的参数放在冒号之后,始终放在双引号内。例如:

{{ bio|truncatewords:"30" }}

上述示例显示 bio 变量的前 30 个词。

下面是几个最重要的过滤器。

• addslashes :在反斜线、单引号和双引号前面添加一个反斜线。可用于转义字符串。例如: {{ val-
ue|addslashes }} 。
• date :根据参数中的格式字符串格式化 date 或 datetime 对象。例如: {{ pub_date|date:"F j, Y"
}} 。格式字符串在附录 E 中说明。
• length :返回值的长度。对列表来说,返回元素的数量。对字符串来说,返回字符的数量。如果变量
未定义,返回 0 。

 Django Template Language(DTL)设计理念

Django 发端于在线新闻站点,其特点是大容量、变化频繁。最初设计 Django 的人对 DTL 有非常明确的理念预设

如今,这些理念仍然是 Django 的核心。它们是:
1. 表现与逻辑分离
2. 避免重复
3. 与 HTML 解耦
4. XML 不好
5. 不要求具备设计能力
6. 透明处理空格
7. 不重造一门编程语言
8. 确保安全有保障
9. 可扩展

下面分述各点

1. 表现与逻辑分离

模板系统用于控制表现及与其相关的逻辑,仅此而已。超出这一基本目标的功能都不应该支持。

2. 避免重复

大多数动态网站都使用某种全站通用的设计,例如通用的页头、页脚、导航栏,等等。Django 模板系
统应该为此提供便利的方式,把这些元素存储在一个位置,减少重复的代码。模板继承背后就是这个
理念。
3. 与 HTML 解耦

模板系统不应该只能输出 HTML,还要能够生成其他基于文本的格式(也就是纯文本)。
4. XML 不应该作为模板语言

如果使用 XML 引擎解析模板,编辑模板时可能引入大量人为错误,而且处理模板有很多额外消耗。
5. 不要求具备设计能力

模板系统不应该必须在 WYSIWYG 编辑器(如 Dreamweaver)中才能写出。这样有太多局限,句法不够灵活。Django 的目标是让模板编写人员能直接编辑 HTML。
6. 透明处理空格

模板系统不应该特殊处理空格。模板中的空格就是空格,要像文本那样显示出来。不在模板标签中的空格都应该显示。
7. 不重造一门编程语言

模板系统一定不能允许:
◦ 为变量赋值
◦ 编写高级的逻辑
也就是不能重造一门编程语言。模板系统的目标是提供适量的编程功能,例如分支和循环,足够做表现相关的判断就行。Django 模板系统知道模板最常由设计师编写,而不是程序员,因此不要求具备 Python 知识。
8. 安全保障

模板系统默认应该禁止包含恶意代码,例如删除数据库记录的命令。这是模板系统不允许随意使用Python 代码的另一个原因。
9. 可扩展

模板系统应该认识到,高级模板编写人员可能想扩展功能。这是自定义模板标签和过滤器背后的理念。

在视图中使用模板

模板加载机制

为了从文件系统中加载模板,Django 提供了便利而强大的 API,力求去掉模板加载调用和模板自身的冗余。若想使用这个模板加载 API,首先要告诉框架模板的存储位置。这个位置在设置文件中配置,即前一章介绍ROOT_URLCONF 设置时提到的 settings.py 文件。打开 settings.py 文件,找到TEMPLATES设置。它的值是一个列表,分别针对各个模板引擎:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            # ... 一些选项 ...
        },
    },
]

 模板目录

DIRS 的默认值是一个空列表。为了告诉 Django 的模板加载机制到哪里寻找模板,选择一个选保存模板的目录,把它添加到 DIRS 中,像下面这样:

'DIRS': [
    '/home/html/example.com',
    '/home/html/default',
],

有几点要注意:

• 如果不是构建没有应用的极简程序,最好留空 DIRS 。设置文件默认把 APP_DIRS 设为 True ,因此最好在 Django 应用中放一个“templates”子目录。
• 如果想在项目根目录中放一些主模板(例如在 mysite/templates 目录中),需要像这样设定 DIRS :

'DIRS': [os.path.join(BASE_DIR, 'templates')],

• 模板目录不一定非得叫 'templates' ,Django 不限制你用什么名称,但是坚守约定易于理解项目的结构。如果不想遵守这个约定,或者出于某些原因不这么做,可以指定任何目录,只要启动 Web 服务器的用户有权读取那个目录中的模板就行
• 在 Windows 中要加上盘符,而且要使用 Unix 风格的正斜线,而不是反斜线,如下所示:

'DIRS': ['C:/www/django/templates']

回到 current_datetime 视图,把它改成下面这样:

from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
import datetime
def current_datetime(request):
    now = datetime.datetime.now()
    t = get_template('current_datetime.html')
    html = t.render(Context({'current_date': now}))
    return HttpResponse(html)

为了找到模板在文件系统中的位置, get_template() 按下列顺序查找:

• 如果 APP_DIRS 的值是 True ,而且使用 DTL,在当前应用中查找“templates”目录。
• 如果在当前应用中没找到模板, get_template() 把传给它的模板名称添加到 DIRS 中的各个目录后面,按顺序在各个目录中查找。假如 DIRS 的第一个元素是 '/home/django/mysite/templates' ,上述get_template() 调用查找的模板是 /home/django/mysite/templates/current_datetime.html 。
• 如果 get_template() 找不到指定名称对应的模板,抛出 TemplateDoesNotExist 异常。

接下来,创建 current_datetime.html 文件,写入下述模板代码:

It is now {{ current_date }}.

把这个文件保存到 mysite/templates 目录中(如果没有“templates”目录,创建一个)。然后在 Web 浏览器中
刷新页面,此时应该能看到正确渲染的页面。

目录结构

setting.py修改

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

views.py修改

def current_datetime(request):
    now = datetime.datetime.now()
    t = get_template('current_datetime.html')
    html = t.render({'current_date': now})
    return HttpResponse(html)

 current_datetime.html修改

It is now {{ current_date }}.

urls.py修改

urlpatterns = [
    url(r'^$', hello),
    url(r'^admin/', admin.site.urls),
    url(r'^hello/$',hello),
    url(r'^time/plus/(\d+)/$', hours_ahead),
    url(r'^time/', current_datetime),
]

运行结果

render()

下面是使用 render() 重写的 current_datetime 视图:

from django.shortcuts import render
import datetime
def current_datetime(request):
    now = datetime.datetime.now()
    return render(request, 'current_datetime.html', {'current_date': now})

关于这次改动:

• 不用再导入 get_template 、 Template 、 Context 或 HttpResponse 了,而要导入 django.shortcuts.ren-der 。 import datetime 不变。
• 在 current_datetime 函数中,仍然要计算 now ,不过加载模板、创建上下文、渲染模板和创建 HttpRe-sponse 对象全由 render() 调用代替了。 render() 的返回值是一个 HttpResponse 对象,因此在视图中可以直接返回那个值。

render() 的第一个参数是请求对象,第二个参数是模板名称,第三个单数可选,是一个字段,用于创建传给
模板的上下文。如果不指定第三个参数, render() 使用一个空字典。

模板子目录

把模板放到模板目录的子目录中不是难事。调用 get_template() 时,只需在模板名称前面加上子目录的名称和一条斜线,如下所示:

t = get_template('dateapp/current_datetime.html')

render() 是对 get_template() 的简单包装,因此在 render() 的第二个参数中也可以这么做,如下所示:

return render(request, 'dateapp/current_datetime.html', {'current_date': now})

 include 模板标签

这个标签的作用是引入另一个模板的内容。它的参数是要引入的模板的名称,可以是变量,也可以是硬编码的字符串(放在引号里,单双引号都行)。
只要想在多个模板中使用相同的代码,就可以考虑使用 {% include %} ,去除重复。下面两个示例引入nav.html 模板的内容。二者的作用相同,目的是说既可以使用单引号,也可以使用双引号。

{% include 'nav.html' %}
{% include "nav.html" %}

下述示例引入 includes/nav.html 模板的内容:

{% include 'includes/nav.html' %}

下述示例引入的模板名称由变量 template_name 指定:

{% include template_name %}

如果 {% include %} 标签的参数指定的模板不存在,Django 会做下面两件事中的一件:

• DEBUG 为 True 时,渲染 Django 错误页面,显示 TemplateDoesNotExist 异常。
• DEBUG 为 False 时,静默,那个标签的位置什么也不显示。

模板继承

目前,我们举的示例都是简短的 HTML 片段,但是在真实世界中,你将使用 Django 的模板系统创建整个HTML 页面。这就引出 Web 开发常见的一个问题:在整个网站中,如何减少通用页面区域(如全站导航)的重复和冗余?

这里用到一个你没见过的模板标签: {% block %} 。它的作用很简单,告诉模板引擎,子模板可以覆盖这部分
内容。

 base.html

<!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>

current_datetime.html

{% extends "base.html" %}
{% block title %}The current time{% endblock %}
{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}

原理

模板引擎加载 current_datetime.html 模板时,发现有 {% extends %} 标签,意识到这是一个子模板,因此立即加载父模板,即这里的 base.html 。

加载父模板时,模板引擎发现 base.html 中有三个 {% block %} 标签,然后使用子模板中的内容替换。因此,将使用 {% block title %} 中定义的标题和 {% block content %} 中定义的内容。

注意,这个子模板没有定义 footer 块,因此模板系统使用父模板中的内容。父模板中的 {% block %} 块总是后备内容

继承不影响模板的上下文。也就是说,继承树中的任何模板都能访问上下文中的每一个模板变量。根据需要,继承层级的深度不限。继承经常使用下述三层结构:

1. 创建 base.html 模板,定义网站的整体外观。这个模板的内容很少变化。
2. 为网站中的各个“区域”创建 base_SECTION.html 模板(如 base_photos.html 和 base_forum.html )。这些模板扩展 base.html ,包含各区域专属的样式和设计。
3. 为各种页面创建单独的模板,例如论坛页面或相册。这些模板扩展相应的区域模板。

使用模板继承的一些指导方针:

• 如果模板中有 {% extends %} ,必须是模板中的第一个标签。否则,模板继承不起作用。
• 一般来说,基模板中的 {% block %} 标签越多越好。记住,子模板无需定义父模板中的全部块,因此可以为一些块定义合理的默认内容,只在子模板中覆盖需要的块。钩子多总是好的。
• 如果发现要在多个模板中重复编写相同的代码,或许说明应该把那些代码移到父模板中的一个 {%block %} 标签里。
• 如果需要从父模板中的块里获取内容,使用 {{ block.super }} ,这是一个“魔法”变量,提供父模板中渲染后的文本。向块中添加内容,而不是完全覆盖时就可以这么做。
• 在同一个模板中不能为多个 {% block %} 标签定义相同的名称。之所以有这个限制,是因为 block 标签是双向的。即, block 标签不仅标识供填充的空位,还用于定义填充父模板中空位的内容。如果一个模板中有两个同名的块,那么父模板就不知道使用哪个块里的内容。
• 传给 {% extends %} 的模板名称使用与 get_template() 相同的方法加载。即,模板在 DIRS 设置定义的目录中,或者在当前 Django 应用的“templates”目录里。
• 多数情况下, {% extends %} 的参数是字符串,不过如果直到运行时才知道父模板的名称,也可以用变量。通过这一点可以做些动态判断。

猜你喜欢

转载自blog.csdn.net/Da___Vinci/article/details/84284895