Django-模板

1.模板的介绍

你可能注意到了,前一章在示例视图中返回文本的方式有些特别,即在 Python 代码中硬编码 HTML,如下所
示:

def current_datetime(request):
now = datetime.datetime.now()
html = "It is now %s." % now
return HttpResponse(html)

这样做虽然便于说明视图的工作方式,但是直接在视图中硬编码 HTML 不是个好主意。原因如下:
只要想修改页面的设计就要修改 Python 代码。网站的设计肯定比底层的 Python 代码变化频繁,因此
如果无需修改 Python 代码就能改变设计,那多方便。
这只是十分简单的示例。网页模板往往包含几百行 HTML 和脚本。在这样的混乱中排错和诊断程序代
码简直是噩梦。(我说的是 PHP 吗?)
编写 Python 代码和设计 HTML 是两件不同的事,多数专业的 Web 设计团队会把这两件事交给不同的
人做(甚至不同的部门)。设计师和 HTML/CSS 程序员完成工作不应该需要编辑 Python 代码。
如果编写 Python 代码的程序员和设计模板的设计师能同时工作,而不用等到一个人编辑好包含 Python
HTML 的文件之后再交给下一个人,工作效率能得到提升。
鉴于此,把页面的设计与 Python 代码分开,结果更简洁,也更易于维护。为此,我们可以使用 Django 的模
板系统。

2.模板的基础

1.介绍

Django 模板是一些文本字符串,作用是把文档的表现与数据区分开。模板定义一些占位符和基本的逻辑(模
板标签),规定如何显示文档。通常,模板用于生成 HTML,不过 Django 模板可以生成任何基于文本的格
式。

#Django 模板背后的哲学
#如果你有编程背景,或者用过把程序代码直接混入 HTML 的语言,要记住一件事:Django 的
#模板可不是把 Python 代码嵌入 HTML 这么简单。模板系统的设计目的是呈现表现,而不是程
#序逻辑

Django的模板:HTML代码+模板语法

模版包括在使用时会被值替换掉的 变量,和控制模版逻辑的 标签

2.变量和标签的详细说明

"""
• 两对花括号包围的文本(如 {{ person_name }})是变量,意思是“把指定变量的值插入这里”。如何指
定变量的值呢?稍后说明。
• 一对花括号和百分号包围的文本(如 {% if ordered_warranty %})是模板标签。 标签的定义相当宽泛:只要能让模板系统“做些事”的就是标签。
• 这个示例模板中有一个 for 标签({% for item in item_list %})和一个 if 标签({% if ordered_warranty %})。for 标签的作用与 Python 中的 for 语句很像,用于迭代序列中的各个元素。与 你预期的一样,if 标签的作用是编写逻辑判断语句。
这里,if 标签检查 ordered_warranty 变量的求值结果是不是 True。如果是,模板系统将显示 {% if ordered_warranty %} 和 {% else %} 之间的内容;如果不是,模板系统将显示 {% else %} 和 {% endif %} 之间的内容。注意,{% else %} 是可选的。
• 最后,这个模板的第二段包含一个过滤器,这是调整变量格式最为便利的方式。对这个示例中的 {{ ship_date|date:"F j, Y" }} 来说,我们把 ship_date 变量传给 date 过滤器,并且为 date 过滤器指定 "F j, Y" 参数。date 过滤器使用参数指定的格式格式化日期。过滤器使用管道符号(|)依附,类似 于 Unix 管道。
Django 模板能访问多个内置的标签和过滤器,你最好熟悉一下,知道有哪些可用。我们自己也可以创建过滤器和标签,
"""

 

3.模板语言中句点符(.)的应用:

在 Django 模板中遍历复杂数据结构的关键是句点符  

例如:

在视图函数中:

def index(request):
    import datetime
    s="hello"
    l=[111,222,333]    # 列表
    dic={"name":"yuan","age":18}  # 字典
    date = datetime.date(1993, 5, 2)   # 日期对象
 
    class Person(object):
        def __init__(self,name):
            self.name=name
 
    person_yuan=Person("yuan")  # 自定义类对象
    person_egon=Person("egon")
    person_alex=Person("alex")
 
    person_list=[person_yuan,person_egon,person_alex]
 
 
    return render(request,"index.html",{"l":l,"dic":dic,"date":date,"person_list":person_list})

5.在模板文件中template:

<h4>{{s}}</h4>
<h4>列表:{{ l.0 }}</h4>
<h4>列表:{{ l.2 }}</h4>
<h4>字典:{{ dic.name }}</h4>
<h4>日期:{{ date.year }}</h4>
<h4>类对象列表:{{ person_list.0.name }}</h4>


#句点符后面可以跟索引,字典中的键,还有类的实例中的属性

#渲染出来的结果分别是索引对应的值,键值,以及类属性中对应的值

"""
注意:句点符也可以用来引用对象的方法(无参数方法)。

    <h4>字典:{{ dic.name.upper }}</h4>


对象方法中一定要是没有参数的才可以用

"""

6.模板语言中的过滤器

default

如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值。例如:

{{ value|default:"nothing" }}

length

返回值的长度。它对字符串和列表都起作用。

{{ value|length }}

filesizeformat

将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB''4.1 MB''102 bytes', 等等)。例如:

{{ value|filesizeformat }}

如果 value 是 123456789,输出将会是 117.7 MB

date

#如果 value=datetime.datetime.now()

{{ value|date:"Y-m-d" }}  

slice

#如果 value="hello world"    

{{ value|slice:"2:-1" }}

truncatechars

#如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。

#参数:要截断的字符数

#例如:
    
{{ value|truncatechars:9 }}

#截断value的前9个字符

safe

#Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,
这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,
比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可
能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,
如果自动转义的话显示的就是保护HTML标签的源文件。
为了在Django中关闭HTML的自#动转义有两种方式,
如果是一个单独的变量我们可以通过过滤器“|safe”的方#式告诉Django这段代码是安全的不必转义。比如:
value="<a href="">点击</a>" {{ value|safe}}

7.模板中的标签

标签看起来像是这样的: {% tag %}。标签比变量更加复杂:一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。

一些标签需要开始和结束标签 

标签的语法:

{% tag %}

...标签 内容 ...

{% endtag %}

 

for标签(遍历灭一个元素)

1.迭代数组中的各个元素,并把当前迭代的元素赋值给一个上下文变量。例如,下述代码列出 athlete_list

<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>

2.可以使用 {% for obj in list reversed %} 反向迭代列表。如果需要迭代列表构成的列表,可以把各个子列
表拆包出来,赋予单独的变量。访问字典中的元素时也可以这么做。假如上下文中有个名为 data 的字典,下
述代码显示它的键和值: 

的各位运动员:

{% for key, value in data.items %}
{{ key }}: {{ value }}
{% endfor %}

注意:循环序号可以通过{{forloop}}显示

{% for %} 循环内部,可以访问一个名为 forloop 的模板变量。这个变量有几个属性,通过它们可以获知
循环进程的一些信息: 

#• forloop.counter 的值是一个整数,表示循环的次数。这个属性的值从 1 开始,因此第一次循环时,
#forloop.counter 等于 1。下面举个例子:

{% for item in todo_list %} <p>{{ forloop.counter }}: {{ item }}</p> {% endfor %}
#• forloop.counter0 与 forloop.counter 类似,不过是从零开始的。第一次循环时,其值为 0。 #• forloop.revcounter 的值是一个整数,表示循环中剩余的元素数量。第一次循环时,forloop.revcounter 的值是序列中要遍历的元素总数。最后一次循环时,forloop.revcounter 的值为 1。 #• forloop.revcounter0 与 forloop.revcounter 类似,不过索引是基于零的。第一次循环时,forloop.revcounter0 的值是序列中元素数量减去一。最后一次循环时,forloop.revcounter0 的值为 0。 #• forloop.first 是个布尔值,第一次循环时为 True。需要特殊处理第一个元素时很方便:
#• forloop.first 是个布尔值,第一次循环时为 True。需要特殊处理第一个元素时很方便:
{% for object in objects %}
{% if forloop.first %}
<li class="first">
{% else %}
<li>
{% endif %}
{{ object }}
</li>
{% endfor %}
#forloop.last 是个布尔值,最后一次循环时为 True。经常用它在一组链接之间放置管道符号:
{% for link in links %}
{{ link }}{% if not forloop.last %} | {% endif %}
{% endfor %}

#结果:Link1 | Link2 | Link3 | Link4
#• 在嵌套的循环中,forloop.parentloop 引用父级循环的 forloop 对象。下面举个例子:
{% for country in countries %}
<table>
{% for city in country.city_list %}
<tr>
<td>Country #{{ forloop.parentloop.counter }}</td>
<td>City #{{ forloop.counter }}</td>
<td>{{ city }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}
#forloop 变量只在循环内部可用。模板解析器遇到 {% endfor %} 时,forloop 随之消失。

注意:

forloop 变量只在循环内部可用。模板解析器遇到 {% endfor %} 时,forloop 随之消失。

for … empty 

for 标签有个可选的 {% empty %} 子句,在指定的数组为空或无法找到时显示一些文本。

<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% empty %}
<li>Sorry, no athletes in this list.</li>
{% endfor %}
</ul>

if

{% if %} 计算变量的值,如果为真(即存在、不为空、不是假值),输出块中的内容:

"""
{% if athlete_list %}
Number of athletes: {{ athlete_list|length }}
{% elif athlete_in_locker_room_list %}
Athletes should be out of the locker room soon!
{% else %}
No athletes.
{% endif %}
"""

在上述代码中,如果 athlete_list 不为空,{{ athlete_list|length }} 将显示运动员的数量。可以看出,if
标签可以有一个或多个 {% elif %} 子句,以及一个 {% else %} 子句,在前述条件判断都失败时显示。这些子
句都是可选的。

模板语言中布尔运算符的问题:

if 标签可以使用 andor not 测试多个变量或取反指定的变量:

"""
{% if athlete_list and coach_list %}
Both athletes and coaches are available.
{% endif %}
{% if not athlete_list %}
There are no athletes.
{% endif %}
{% if athlete_list or coach_list %}
There are some athletes or some coaches.
{% endif %}
"""

同一个标签中可以同时使用 and or,前者的优先级较高。例如:

{% if athlete_list and coach_list or cheerleader_list %}

是这样解释的:

if (athlete_list and coach_list) or cheerleader_list

但是,在 if 标签中使用 括号 是 无效 的句法。如果想指明优先级,应该使用嵌套的 if 标签。

 

with

使用一个简单地名字缓存一个复杂的变量,当你需要使用一个“昂贵的”方法(比如访问数据库)很多次的时候是非常有用的,例如:

"""
{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}
"""

csrf_token

这个标签提供 CSRF 防护(跨站请求伪造保护)

3.模板继承

Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可以让您创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。

通过从下面这个例子开始,可以容易的理解模版继承:

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}My amazing site{%/span> endblock %}</title>
</head>

<body>
    <div id="sidebar">
        {% block sidebar %}
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
        {% endblock %}
    </div>

    <div id="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>

这个模版,我们把它叫作 base.html, 它定义了一个可以用于两列排版页面的简单HTML骨架。“子模版”的工作是用它们的内容填充空的blocks。

在这个例子中, block 标签定义了三个可以被子模版内容填充的block。 block 告诉模版引擎: 子模版可能会覆盖掉模版中的这些位置。

子模版可能看起来是这样的:

{% extends "base.html" %}
 
{% block title %}My amazing blog{% endblock %}
 
{% block content %}
{% for entry in blog_entries %}
    <h2>{{ entry.title }}</h2>
    <p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

extends 标签是这里的关键。它告诉模版引擎,这个模版“继承”了另一个模版。当模版系统处理这个模版时,首先,它将定位父模版——在此例中,就是“base.html”。

那时,模版引擎将注意到 base.html 中的三个 block 标签,并用子模版中的内容来替换这些block。根据 blog_entries 的值,输出可能看起来是这样的:

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>My amazing blog</title>
</head>
 
<body>
    <div id="sidebar">
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
    </div>
 
    <div id="content">
        <h2>Entry one</h2>
        <p>This is my first entry.</p>
 
        <h2>Entry two</h2>
        <p>This is my second entry.</p>
    </div>
</body>
</html>

请注意,子模版并没有定义 sidebar block,所以系统使用了父模版中的值。父模版的 {% block %} 标签中的内容总是被用作备选内容(fallback)。

总结:使用模板继承的时候,要在基板内写好那些{% block%}块可以被继承,子模板可以是父模板的这些内容,也可以用自己的。(子模板开头一定要写:

{% extends "base.html" %}

 

4.模板语言的加载机制

1.介绍

为了从文件系统中加载模板,Django 提供了便利而强大的 API,力求去掉模板加载调用和模板自身的冗余。
若想使用这个模板加载 API,首先要告诉框架模板的存储位置。这个位置在设置文件中配置,即前一章介绍
ROOT_URLCONF 设置时提到的 settings.py 文件。打开 settings.py 文件,找到 TEMPLATES 设置。它的值是一个
列表,分别针对各个模板引擎:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
# ... 一些选项 ...
},
},
]
BACKEND 的值是一个点分 Python 路径,指向实现 Django 模板后端 API 的模板引擎类。内置的后端有 django.template.backends.django.DjangoTemplates django.template.backends.jinja2.Jinja2
因为多数引擎从文件中加载模板,所以各个引擎的顶层配置包含三个通用的设置:
DIRS 定义一个目录列表,模板引擎按顺序在里面查找模板源文件。
APP_DIRS 设定是否在安装的应用中查找模板。按约定,APPS_DIRS 设为 True 时,DjangoTemplates 会在
INSTALLED_APPS 中的各个应用里查找名为“templates”的子目录。这样,即使 DIRS 为空,模板引擎还能
查找应用模板。
OPTIONS 是一些针对后端的设置。
同一个后端可以配置具有不同选项的多个实例,然而这并不常见。此时,要为各个引擎定义唯一的 NAME

 

2.模板子目录

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'],
我们还未创建 Django 应用,因此为了让下面的代码能正常运行,要把 DIRS 设为
[os.path.join(BASE_DIR,'templates')]

 

5.模板继承的的补充

继承不影响模板的上下文。也就是说,继承树中的任何模板都能访问上下文中的每一个模板变量。根据需
要,继承层级的深度不限。继承经常使用下述三层结构:
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 %} 的参数是字符串,不过如果直到运行时才知道父模板的名称,也可以用变
量。通过这一点可以做些动态判断。

 

模板语言的注释:

HTML Python 一样,Django 模板语言支持注释。注释使用 {# #} 标明:
{# This is a comment #}
渲染模板时,不输出注释。使用这种句法编写的注释不能分成多行。这一限制有助于提升模板解析性能。
在下述模板中,渲染的结果与模板完全一样(即注释标签不会解析为注释):
This is a {# this is not
a comment #}
test.
如果想编写多行注释,使用 {% comment %} 模板标签,如下所示:
{% comment %}
This is a
multi-line comment.
{% endcomment %}
注释标签不能嵌套。

 

 

 

 

 

 

 

 

 

 

posted @ 2018-01-06 17:29  明-少  阅读(112)  评论(0)    收藏  举报