模板系统基本知识
模板是一个文本文件(可以是HTML,XML,CSV等任何文本格式),同时包含了静态内容(例如HTML)和动态标记的逻辑,用于分离文档的表现形式和内容。 模板定义了占位符以及各种用于规范文档该如何显示的各部分基本逻辑(模板标签)。 模板通常用于产生HTML,但是Django的模板也能产生任何基于文本格式的文档。使用哪个模版以及渲染什么数据是由视图函数本身(通过显式的渲染或者使用render_to_response)或者视图的参数(比如通用视图里的template_name参数)决定的。
Django的模版语言是给前端工程师使用的,他们不一定是程序员。再加上将逻辑和表现分开的愿望,模版语言绝对不是嵌入式的Python
举个例子。 该模板描述了一个向某个与公司签单人员致谢 HTML 页面:
<html>
<head><title>Ordering notice</title></head>
<body>
<h1>Ordering notice</h1>
<p>Dear {{ person_name }},</p> {#用两个大括号括起来的文字称为变量(variable) 。这意味着在此处插入指定变量的值。 #}
<p>Thanks for placing an order from {{ company }}. It‘s scheduled to
ship on {{ ship_date|date:"F j, Y" }}.</p> {#这是一个filter过滤器的例子#}
<p>Here are the items you‘ve ordered:</p>
<ul>
{% for item in item_list %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% if ordered_warranty %} {#被大括号和百分号包围的文本是模板标签。标签(tag)定义比较明确,即: 仅通知模板系统完成某些工作的标签。#}
<p>Your warranty information will be included in the packaging.</p>
{% else %}
<p>You didn‘t order a warranty, so you‘re on your own when
the products inevitably stop working.</p>
{% endif %}
<p>Sincerely,<br />{{ company }}</p>
</body>
</html>
以上就是一个模版。该模板是一段添加了些许变量和模板标签的基础 HTML 。
用两个大括号括起来的文字(例如 { { person_name }} )称为 变量(variable) 。这意味着在此处插入指定变量的值。
被大括号和百分号包围的文本(例如 {% if ordered_warranty %} )是 模板标签(template tag) 。标签(tag)定义比较明确,即: 仅通知模板系统完成某些工作的标签。这个例子中的模板包含一个for标签( {% for item in item_list %} )和一个if 标签( {% if ordered_warranty %} )
for标签类似Python的for语句,可让你循环访问序列里的每一个项目。
if 标签,正如你所料,是用来执行逻辑判断的。 在这里, tag标签检查ordered_warranty值是否为True。如果是,模板系统将显示{% if ordered_warranty %}和{% else %}之间的内容;否则将显示{% else %}和{% endif %}之间的内容。 {% else %}是可选的。
最后,这个模板的第二段中有一个关于filter过滤器的例子,它是一种最便捷的转换变量输出格式的方式。如这个例子中的{{ship_date|date:”F j, Y” }},我们将变量ship_date传递给date过滤器,同时指定参数”Fj,Y”。 date过滤器根据参数进行格式输出。 过滤器是用管道符(|)来调用的,用法类似于Unix管道符。
使用模板
看看下面的例子
>>> from django import template
>>> from django.conf import settings #python需告诉Django使用哪个设置文件,否则不能使用template
>>> text1=template.Template(‘大家好我是{{name}}‘) #创建Template对象
>>> text2=template.Context({‘name‘:"王大锤"}) #使用Context传递数据
>>> text1.render(text2)
‘大家好我是王大锤‘
这就是使用Django模板系统的基本规则: 写模板,创建 Template 对象,创建 Context , 调用 render() 方法
基本的标签和过滤器
标签
if/else
{ % if %} 标签检查(evaluate)一个变量,如果这个变量为真(即,变量存在,非空,不是布尔值假),系统会显示在 {% if %} 和 {% endif %} 之间的任何内容,例如:
{% if today_is_weekend %}
<p>Welcome to the weekend!</p>
{% else %} {#{% else %}是可选的#}
<p>Get back to work.</p>
{% endif %}
{ % if %} 标签接受 and , or 或者 not 关键字来对多个变量做判断 ,或者对变量取反( not ),但是{% if %} 标签不允许在同一个标签中同时使用 and 和 or ,因为逻辑上可能模糊的,例如,如下示例是错误的: 比如这样的代码是不合法的:
{% if athlete_list and coach_list or cheerleader_list %} {#这个是错误的#}
需要改成这样
{% if athlete_list %}
{% if coach_list or cheerleader_list %}
We have athletes, and either coaches or cheerleaders!
{% endif %}
{% endif %}
for
{% for %} 允许我们在一个序列上迭代。 与Python的 for 语句的情形类似,循环语法是 for X in Y ,Y是要迭代的序列而X是在每一个特定的循环中使用的变量名称。 每一次循环中,模板系统会渲染在 {% for %} 和{% endfor %} 之间的所有内容。例如
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li> {#输出所有运动员的名字#}
{% endfor %}
</ul>
在执行循环之前先检测列表的大小是一个通常的做法,当列表为空时输出一些特别的提示。
{% if athlete_list %}
{% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
{% endfor %}
{% else %}
<p>There are no athletes. Only computer programmers.</p>
{% endif %}
下面引入{% empty %}标签,遇上面这个例子效果相同
{% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
{% empty %}
<p>There are no athletes. Only computer programmers.</p>
{% endfor %}
forloop模版变量
forloop.counter 总是一个表示当前循环的执行次数的整数计数器。 这个计数器是从1开始的,所以在第一次循环时forloop.counter 将会被设置为1。因为Django不支持break、continue,因此需要使用forloop.counter 实现他们的功能
forloop.revcounter是表示循环中剩余项的整型变量。 在循环初次执行时forloop.revcounter将被设置为序列中项的总数。 最后一次循环执行中,这个变量将被置1。
forloop.parentloop 是一个指向当前循环的上一级循环的forloop对象的引用(在嵌套循环的情况下)。
{% for country in countries %}
<table>
{% for city in country.city_list %}
<tr>
<td>Country #{{ forloop.parentloop.counter }}</td> {#获得上一级的counter#}
<td>City #{{ forloop.counter }}</td>
<td>{{ city }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}
ifequal/ifnotequal
比较两个值,当他们相等时,显示在 {% ifequal %} 和 {% endifequal %} 之中所有的值。只有模板变量,字符串,整数和小数可以作为 {% ifequal %} 标签的参数。
{% ifequal section ‘sitenews‘ %}
<h1>Site News</h1>
{% else %}
<h1>No News Here</h1>
{% endifequal %}
模板继承———block 与extends
模板可以用继承的方式来实现复用。
<html>
<head>
<title>test</title>
</head>
<body>
<h1>come from base.html</h1>
{% block mainbody %} {#声明block块#}
<p>original</p>
{% endblock %} {#块的结尾#}
</body>
</html>
该页面中,名为mainbody的block标签是可以被继承者们替换掉的部分。
{% extends "base.html" %} {#继承base.html文件#}
{% block mainbody %} {#重写block块#}
{% for item in staffs %}
<p>{{ item.id }},{{ item.name }}</p>
{% endfor %}
{% endblock %}
你可以根据需要使用任意多的继承次数。 使用继承的一种常见方式是下面的三层法:
1. 创建 base.html 模板,在其中定义站点的主要外观感受。 这些都是不常修改甚至从不修改的部分。
2. 为网站的每个区域创建 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。这些模板对 base.html 进行拓展,并包含区域特定的风格与设计。
3. 为每种类型的页面创建独立的模板,例如论坛页面或者图片库。 这些模板拓展相应的区域模板。这个方法可最大限度地重用代码,并使得向公共区域(如区域级的导航)添加内容成为一件轻松的工作。
以下是使用模板继承的一些诀窍:
如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。
一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。
如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。如果你需要访问父模板中的块的内容,使用{{ block.super }}这个标签吧,这一个变量将会表现出父模板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。 也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。
include
该标签允许在模版中包含其他的模版内容,标签的参数是所要包含模版的名称,可以使变量,可以使引号加文件名。
# mypage.html
<html>
<body>
{% include "includes/nav.html" %}
<h1>{{ title }}</h1>
</body>
</html>
# includes/nav.html
<div id="nav">
You are in: {{ current_section }}
</div>
如果你用一个包含 current_section的上下文去渲染 mypage.html这个模板文件,这个变量将存在于它所包含(include)的模板里
如果{% include %}标签指定的模板没找到,Django将会在下面两个处理方法中选择一个:
如果 DEBUG 设置为 True ,你将会在 Django 错误信息页面看到 TemplateDoesNotExist 异常。
如果 DEBUG 设置为 False ,该标签不会引发错误信息,在标签位置不显示任何东西。
过滤器
1.可以通过过滤器来修改变量的显示,过滤器的形式是:{{ variable | filter }},管道符号’|’代表使用过滤器
2.过滤器能够采用链式的方式使用,例如:{{ text | escape | linebreaks }}
3.过滤器还可以带参数,例如: {{ bio|truncatewords:30 }}
4.过滤器的参数中如果带有空格,那么需要用引号引起来,例如:{{ list | join : “, “}}
5.django中50个内建的过滤器
(1)add
使用形式为:{{ value | add: "2"}}
意义:将value的值增加2
(2)addslashes
使用形式为:{{ value | addslashes }}
意义:在value中的引号前增加反斜线
(3)capfirst
使用形式为:{{ value | capfirst }}
意义:value的第一个字符转化成大写形式
(4)cut
使用形式为:{{ value | cut:arg}}, 例如,如果value是“String with spaces” arg是" "那么输出是"Stringwithspaces"
意义:从给定value中删除所有arg的值
(5)date
使用形式为::
(a) {{ value | date:"D d M Y" }},例如,如果value是一个datetime对象(datetime.datetime.now())那么输出将是字符串"Wed 09 Jan 2008"
(b) {{ value | date }},这种形式没有格式化字符串,这时候,格式化字符串会自动采用DATE_FORMAT所设置的形式。
意义:将日期格式数据按照给定的格式输出
(6)default
使用形式:{{ value | default: "nothing" }},例如,如果value是“”,那么输出将是nothing
意义:如果value的意义是False,那么输出使用缺省值
(7)default_if_none
使用形式:{{ value | default_if_none:"nothing" }},例如,如果value是None,那么输出将是nothing
意义:如果value是None,那么输出将使用缺省值
(8)dictsort
意义:如果value的值是一个字典,那么返回值是按照关键字排序的结果
使用形式:{{ value | dictsort:"name"}},例如,
如果value是:
[
{‘name‘: ‘zed‘, ‘age‘: 19},
{‘name‘: ‘amy‘, ‘age‘: 22},
{‘name‘: ‘joe‘, ‘age‘: 31},
]
那么,输出是:
[
{‘name‘: ‘amy‘, ‘age‘: 22},
{‘name‘: ‘joe‘, ‘age‘: 31},
{‘name‘: ‘zed‘, ‘age‘: 19},
]
(9)dictsortreversed
意义:如果value的值是一个字典,那么返回值是按照关键字排序的结果的反序
使用形式:与上述(8)完全相同。
(10)divisibleby
使用形式:{{ value | divisibleby:arg}},如果value是21,arg是3,那么输出将是True
意义:如果value能够被arg整除,那么返回值将是True
(11)escape
使用形式:{{ value | escape}}
意义:替换value中的某些字符,以适应HTML格式,包括:
< is converted to <
> is converted to >
‘ (single quote) is converted to '
" (double quote) is converted to "
& is converted to &
escape仅仅在输出的时候才起作用,所以escape不能够用在链式过滤器的中间, 他应该总是最后一个过滤器,如果想在链式过滤器的中间使用,那么可以使用force_escape
(12)escapejs
使用形式:{{ value | escapejs }}
意义:替换value中的某些字符,以适应JAVASCRIPT和JSON格式。
(13)filesizeformat
使用形式:{{ value | filesizeformat }}
意义:格式化value,使其成为易读的文件大小,例如:13KB,4.1MB等。
(14)first
使用形式:{{ value | first }}
意义:返回列表中的第一个Item,例如,如果value是列表[‘a‘,‘b‘,‘c‘],那么输出将是‘a‘。
(15)floatformat
使用形式:{{ value | floatformat}}或者{{value|floatformat:arg}},
arg可以是正数也可以是负数。没有参数的floatformat相当于floatformat:-1
(1)如果不带arg,那么引擎会四舍五入,同时最多只保留一位小数。
34.23234 {{ value|floatformat }} 34.2
34.00000 {{ value|floatformat }} 34
34.26000 {{ value|floatformat }} 34.3
(2)如果arg是正数,那么引擎会四舍五入,同时保留arg位的小数。
34.23234 {{ value|floatformat:3 }} 34.232
34.00000 {{ value|floatformat:3 }} 34.000
34.26000 {{ value|floatformat:3 }} 34.260
(3)如果arg是负数,那么引擎会四舍五入,如果有小数部分,那么保留arg位小数;否则,则没有任何小数部分。
34.23234 {{ value|floatformat:"-3" }} 34.232
34.00000 {{ value|floatformat:"-3" }} 34
34.26000 {{ value|floatformat:"-3" }} 34.26
(16)get_digit使用形式:{{ value | get_digit:"arg"}},例如,如果value是123456789,arg是2,那么输出是8
意义:给定一个数字,返回,请求的数字,记住:1代表最右边的数字,如果value不是合法输入,那么会返回所有原有值。
(17)iriencode 使用形式:{{value | iriencode}}
意义:如果value中有非ASCII字符,那么将其进行抓化成URL中适合的编码,如果value已经进行过URLENCODE,改操作就不会再起作用。
(18)join使用形式:{{ value | join:"arg"}},如果value是[‘a‘,‘b‘,‘c‘],arg是‘//‘那么输出是a//b//c
意义:使用指定的字符串连接一个list,作用如同python的str.join(list)
(19)last使用形式:{{ value | last }}
意义:返回列表中的最后一个Item
(20)length使用形式:{{ value | length }}
意义:返回value的长度。
(21)length_is使用形式:{{ value | length_is:"arg"}}
意义:返回True,如果value的长度等于arg的时候,例如:如果value是[‘a‘,‘b‘,‘c‘],arg是3,那么返回True
(22)linebreaks使用形式:{{value|linebreaks}}
意义:value中的"\n"将被<br/>替代,并且整个value使用</p>包围起来,从而适和HTML的格式
(23)linebreaksbr使用形式:{{value |linebreaksbr}}
意义:value中的"\n"将被<br/>替代
(24)linenumbers使用形式:{{value | linenumbers}}
意义:显示的文本,带有行数。
(25)ljust使用形式:{{value | ljust}}意义:在一个给定宽度的字段中,左对齐显示value
center使用形式:{{value | center}}
意义:在一个给定宽度的字段中,中心对齐显示value
rjust使用形式:{{value | rjust}}
意义:在一个给定宽度的字段中,右对齐显示value
(26)lower使用形式:{{value | lower}}
意义:将一个字符串转换成小写形式
(27)make_list使用形式:{{value | make_list}}
意义:将value转换成一个list,对于字符串,转换成字符list;对于整数,转换成整数list例如value是Joel,那么输出将是[u‘J‘,u‘o‘,u‘e‘,u‘l‘];value是123,那么输出将是[1,2,3]
(28)pluralize使用形式:{{value | pluralize}},或者{{value | pluralize:"es"}},或者{{value | pluralize:"y,ies"}}
意义:如果value不是1,则返回一个复数后缀,缺省的后缀是‘s‘
(29)random使用形式:{{value | random}}
意义:从给定的list中返回一个任意的Item
(30)removetags
使用形式:{{value | removetags:"tag1 tag2 tag3..."}}
意义:删除value中tag1,tag2....的标签。例如,如果value是<b>Joel</b> <button>is</button> a <span>slug</span>
tags是"b span",那么输出将是:Joel <button>is</button> a slug
(31)safe
使用形式:{{value | safe}}
意义:当系统设置autoescaping打开的时候,该过滤器使得输出不进行escape转换
(32)safeseq
与上述safe基本相同,但有一点不同的就是:safe是针对字符串,而safeseq是针对多个字符串组成的sequence
(33)slice
使用形式:{{some_list | slice:":2"}}
意义:与python语法中的slice相同,:2表示第一的第二个元素
(34)slugify
使用形式:{{value | slugify}}
意义:将value转换成小写形式,同事删除所有分单词字符,并将空格变成横线
例如:如果value是Joel is a slug,那么输出将是joel-is-a-slug
(35)stringformat
这个不经常用,先不说
(36)striptags
使用形式:{{value | striptags}}
意义:删除value中的所有HTML标签
(37)time
使用形式:{{value | time:"H:i"}}或者{{value | time}}
意义:格式化时间输出,如果time后面没有格式化参数,那么输出按照TIME_FORMAT中设置的进行。
(38)title
转换一个字符串成为title格式。
(39)truncatewords
使用形式:{{value | truncatewords:2}}
意义:将value切成truncatewords指定的单词数目
例如,如果value是Joel is a slug 那么输出将是:Joel is ...
(40)truncatewords_html
使用形式同(39)
意义:truncation点之前如果某个标签打开了,但是没有关闭,那么在truncation点会立即关闭。
因为这个操作的效率比truncatewords低,所有只有在value是html格式时,才考虑使用。
(41)upper
转换一个字符串为大写形式
(42)urlencode
将一个字符串进行URLEncode
(43)urlize
意义:将一个字符串中的URL转化成可点击的形式。
使用形式:{{ value | urlize }}
例如,如果value是Check out www.djangoproject.com,那么输出将是:
Check out <a href="http://www.djangoproject.com">www.djangoproject.com</a>
(44)urlizetrunc
使用形式:{{ value | urlizetrunc:15}}
意义:与(43)相同,但是有一点不同就是现实的链接字符会被truncate成特定的长度,后面以...现实。
(45)wordcount
返回字符串中单词的数目
46)wordwrap
使用形式:{{value | wordwrap:5}}
意义:按照指定的长度包装字符串
例如,如果value是Joel is a slug,那么输出将会是:
Joel
is a
slug
(47)timesince
使用形式:{{value | timesince:arg}}
意义:返回参数arg到value的天数和小时数
例如,如果 blog_date 是一个日期实例表示 2006-06-01 午夜, 而 comment_date 是一个日期实例表示 2006-06-01 早上8点,
那么 {{ comment_date|timesince:blog_date }} 将返回 "8 hours".
(48)timeuntil
使用形式:{{value | timeuntil}}
意义:与(47)基本相同,一个不同点就是,返回的是value距离当前日期的天数和小时数。
自定义过滤器
在应用文件夹下创建templatetags文件夹,并在里面创建 _ init _ .py,和存放自定义过滤器的.py文件。
在.py文件中定义过滤器如下:
from django import template
register=template.Library()
#方式一
@register.simple_tag
def mymethod1(v1):
return v1*1000
#方式二
@register.filter
def mymethod2(v1):
return v1*1000
在对应的模版.html中导入自定义过滤器
{% load 过滤器文件名%}
使用自定义过滤器
<td>{% mymethod1 item.id %}</td>
<td>{{ item.id|mymethod2}}</td>
在视图中使用模版
我的工作目录为的结构为
mysite/
├── mysite
├── templates
│ └── test
└── welcome
我们拿一个独立的tem.html文件作为模板。它放在templates/test文件夹下。
内容为:
<h1>{{ label }}</h1>
我们需要向Django说明模板文件的搜索路径,修改mysite/settings.py,添加:
# Template dir
TEMPLATE_DIRS = (
os.path.join(BASE_DIR, ‘templates/test/‘), #注意,后面的逗号必不可少!作用是以此消除与圆括号表达式之间的歧义。
)
如果还有其它的路径用于存放模板,可以增加该元组中的元素,以便Django可以找到需要的模板。
我们现在修改welcome/views.py,增加一个新的对象,用于向模板提交数据:
# -*- coding: utf-8 -*-
#from django.http import HttpResponse
from django.shortcuts import render
def tem(request):
context = {}
context[‘label‘] = ‘Hello World!‘
return render(request, ‘tem.html‘, context)
更改mysite/urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
urlpatterns = patterns(‘‘,
# Examples:
# url(r‘^$‘, ‘myTest.views.home‘, name=‘home‘),
# url(r‘^blog/‘, include(‘blog.urls‘)),
url(r‘^admin/‘, include(admin.site.urls)),
url(r‘^templates/test/tem.html‘, ‘Welcome.views.tem‘), #加上这一行
)
访问http://127.0.0.1:8000/templates/test/tem.html,可以看到页面:
补充
1.为什么HTML不能直接在视图中输出呢?比如在视图中加入下面的代码
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
尽管上面的代码可移植性,但是不推荐这样。 这是因为:
1.对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。
2.Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。
3.程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。
2.要通过字典键访问该字典的值,可使用一个句点:
>>> from django.template import Template, Context
>>> person = {‘name‘: ‘Sally‘, ‘age‘: ‘43‘}
>>> t = Template(‘{{ person.name }} is {{ person.age }} years old.‘) #通过建访问值
>>> c = Context({‘person‘: person})
>>> t.render(c)
‘Sally is 43 years old.‘
>>> from django.template import Template, Context
>>> t = Template(‘Item 2 is {{ items.2 }}.‘)
>>> c = Context({‘items‘: [‘apples‘, ‘bananas‘, ‘carrots‘]}) #通过索引访值(不可使用负数索引)
>>> t.render(c)
u‘Item 2 is carrots.‘
点语法也可以用来引用对象的属性、方法。
>>> from django.template import Template, Context
>>> class Person(object):
... def __init__(self, first_name, last_name):
... self.first_name, self.last_name = first_name, last_name
>>> t = Template(‘Hello, {{ person.first_name }} {{ person.last_name }}.‘) #通过属性名访问属性
>>> c = Context({‘person‘: Person(‘John‘, ‘Smith‘)})
>>> t.render(c)
u‘Hello, John Smith.‘
>>> from django.template import Template, Context
>>> t = Template(‘{{ var }} ‐‐ {{ var.upper }} ‐‐ {{ var.isdigit }}‘) #通过方法名访问方法(只能调用无参的方法)
>>> t.render(Context({‘var‘: ‘hello‘}))
u‘hello ‐‐ HELLO ‐‐ False‘
>>> t.render(Context({‘var‘: ‘123‘}))
u‘123 ‐‐ 123 ‐‐ True‘
3.一定要用 {% endif %} 关闭每一个 {% if %} 标签
上一讲:Python网络编程06—-Django中的URLconf