在上一章,我们页面显示的html代码是直接编写在python代码中,因为对页面的设计修改工作要比python频繁的多,所以最好将页面设计工作和python代码分离,所以我们引用模板来实现这个功能。
一、模板实例
下面是一个模板的实例:
<html>
<head><title>Ordering notice</title></head>
<body>
<h1>Ordering notice</h1>
<p>Dear {{ person_name }},</p>
<p>Thanks for placing an order from {{ company }}. It's scheduled to
ship on {{ ship_date|date:"F j, Y" }}.</p>
<p>Here are the items you've ordered:</p>
<ul>
{% for item in item_list %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% if ordered_warranty %}
<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>
分析其中的一些元素:
(1)用大括号括起来的称作:变量,需要对它进行赋值
(2)用大括号和百分号括起来的是:模板标签,即通知模板系统完成某些工作的标签。上文中有两个模板标签:for和if分别实现循环和判断。
(3)第二段有个filter过滤器的使用:
{{ ship_date|date:"F j, Y" }
它是将变量ship_date传递给过滤器date,同时指定参数,过滤器按照参数输出。
二、使用模板
1、基本流程:
(1)用原始的模板代码创建一个Template对象
(2)创建一个Context对象,完成对模板对象中的变量赋值
(3)调用模板对象的render方法,将(2)步生成的context对象作为参数,填充模板,同时返回基于模板的展现字符串,这时其中的变量已经被替换。
实例:
>>> from django import template
>>> t = template.Template('My name is {{ name }}.')
>>> c = template.Context({'name': 'Adrian'})
>>> print t.render(c)
My name is Adrian.
>>> c = template.Context({'name': 'Fred'})
>>> print t.render(c)
My name is Fred.
2、创建模板对象
转到mysite所在的目录,输入
python manage.py shell
为什么我们运行pythonmanage.pyshell而不是python的。这两个命令都会启动交互解释器,但是manage.pyshell命令有一个重要的不同: 在启动解释器之前,它告诉Django使用哪个设置文件。 Django框架的大部分子系统,包括模板系统,都依赖于配置文件;如果Django不知道使用哪个配置文件,这些系统将不能工作。
>>> from django import template
>>> t = template.Template('My name is {{ name }}.')
>>> print t
<django.template.base.Template object at 0x0220EBD0>
每次创建一个template对象,打印出来的地址都不同。
3、模板渲染
即,对模板内的变量、标签赋值。
使用context来传递数据,一个context是一系列变量和他们的值的集合,然后用template的render方法传递context填充模板。
>>> from django import template
>>> t = template.Template('My name is {{ name }}.')
>>> c = template.Context('name': 'Jim')
>>> t.render(c)
u'My name is Jim.'
实例:
>>> from django.template import Template, Context
>>> raw_template = """<p>Dear {{ person_name }},</p>
...
... <p>Thanks for placing an order from {{ company }}. It's scheduled to
... ship on {{ ship_date|date:"F j, Y" }}.</p>
...
... {% if ordered_warranty %}
... <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>"""
>>> t = Template(raw_template)
>>> import datetime
>>> c = Context({'person_name': 'John Smith',
... 'company': 'Outdoor Equipment',
... 'ship_date': datetime.date(2009, 4, 2),
... 'ordered_warranty': False})
>>> t.render(c)
u"<p>Dear John Smith,</p>\n\n<p>Thanks for placing an order from Outdoor
Equipment. It's scheduled to\nship on April 2, 2009.</p>\n\n\n<p>You
didn't order a warranty, so you're on your own when\nthe products
inevitably stop working.</p>\n\n\n<p>Sincerely,<br />Outdoor Equipment
</p>"
输出的结果没有按照\n显示换行,这和python解释器有关,它会按照真实内容显示,如果想显示出换行,使用print t.render(c)
4、一个模板多个渲染一旦定义了一个模板,就可以渲染多个context
>>> from django.template import Template, Context
>>> t = Template('My name is {{ name }}.')
>>> print t.render(Context{'name': 'Jim'})
My name is Jim.
>>> print t.render(Context{'name': 'Pat'})
My name is Pat.
>>> from django.template import Template, Context
>>> t = Template('Hello, {{ name }}')
>>> for name in ('John', 'Juile', 'pat'):
... print t.render(Context({'name': name}))
...
Hello, John
Hello, Juile
Hello, pat
5、深度查找
通过使用点号(.)遍历模板中复杂的数据结构。
5.1访问字典值
>>> 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)
u'Sally is 43 years old.'
5.2访问对象属性
>>> from django.template import Template, Context
>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
1993
>>> d.month
5
>>> d.day
2
>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
u'The month is 5 and the year is 1993.'
5.3访问自定义类的属性
>>> 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.'
5.4引用对象的方法
>>> 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'
每个python字符串都有upper(),lower()和isdigit()方法,分别将字符串转换为大写、小写以及判断是否为数字。这里调用的方法后没有括号,所以只能调用没有参数的方法。
5.5访问列表索引
>>> 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.'
注:不允许使用负数的列表索引
6、句点的查找规则
当模板系统在变量名中遇到句点时,按照以下顺序查找:
- 字典类型查找:如foo["bar"]
- 属性查找:如foo.bar
- 方法调用:foo.bar()
- 列表类型索引查找:如foo[bar]
{% if today_is_weekend %}
<p>Welcome to the weekend!</p>
{% else %}
<p>Get back to work.</p>
{% endif %}
if标签检查变量tody_is_weekend ,如果该变量为真,则执行{% if %}和{% endif %}或者和{% else %}之间的内容。{% else %}是可选的。在python和django模板系统中,以下对象相当于布尔值False:
- 空列表([ ])
- 空元组(( ))
- 空字典({ })
- 空字符串(' ')
- 零值(0)
- 特殊兑现None
- 对象False
{% if athlete_list and coach_list or cheerleader_list %}
同时系统不支持用圆括号来组合比较操作,可以使用嵌套的{% if %} 标签来代替。
{% if athlete_list %}
{% if coach_list or cheerleader_list %}
We have athletes, and either coaches or cheerleaders!
{% endif %}
{% endif %}
可以多次使用同一个逻辑操作符,但是不能把不同的操作符组合起来。例如,以下是合法的:
{% if athlete_list or coach_list or parent_list or teacher_list %}
注意:没有{% elif %}标签,要使用嵌套的{% if %}来达到这个效果,同时每个{% if %}必须用{% endif %}标签结束。
<ul>
{% for athlete in athlete_list%}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
还可以嵌套使用for循环。
{% if athlete_list %}
{% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
<% endfor %>
{% else %}
<p> There are no athletes.</p>
<% endif %>
但是可以使用{% empty %}标签定义列表为空时的输出内容:
{% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
{% empty %}
<p> There are no athletes.</p>
<% endfor %>
这个和上面的例子等价。
注:django不支持退出循环,也不支持continue,可以通过改变正在迭代的变量退出循环。
{% for object in objects %}
{% if forloop.first %}<li class="first">{% else %}<li> <% endif %>
{{ object }}
</li>
{% endfor %}
6)forloop.last:布尔值,在最后一次执行时被置为true
{% for p in places %}{{ p }}{% if not forloop.last %}, {% endif %}{% endfor %}
7)forloop.parentloop:是一个指向当前循环的上一级循环的
forloop 对象的引用(在嵌套循环的情况下)。
{% ifequal variable 1 %}
{% ifequal variable 1.23 %}
{% ifequal variable 'foo' %}
{% ifequal variable "foo" %}
{% ifequal section 'sitenews' %}
<h1>Site News</h1>
{% else %}
<h1>No News Here</h1>
{% endifequal %}
注:布尔、字典、列表类型不能用在{% ifequal %}中。
4、注释
1)单行注释:{# #}
2)多行注释:可以使用三个引号''' 注释内容 '''
过滤器是在变量被显示前,修改它的值。
1、{{ name| lower}} 转换文本为小写
2、过滤管道可以被套接,既是说,一个过滤器管道的输出又可以作为下一个管道的输入,如此下去。
{{ my_list|first|upper }}
查找列表的第一个元素,并转换为大写。
3、带有参数的过滤器:过滤器的参数跟随冒号之后并且总是以双引号包含。
{{ bio|truncatewords:"30"}} 显示变量bio的前30个单词。
4、addslashes : 添加反斜杠到任何反斜杠、单引号或者双引号前面。
5、date : 按指定的格式字符串参数格式化 date 或者 datetime 对象:
{pub_date|date:"F j, Y"}
6、length:返回变量的长度。 对于列表,这个参数将返回列表元素的个数。 对于字符串,这个参数将返回字符串中字符的个数。
五、视图中使用模板
1、
方法:单独建立模板文件, 让视图加载该模板文件。
1)、首先将模板的保存位置告诉框架。打开setting.py 配置文件,找到:TEMPLATE_DIRS,添加模板位置路径。最省事的方法是使用绝对路径,在Django项目中建立模板文件夹templates。
import os.path
TEMPLATE_DIRS = (
os.path.join(os.path.dirname(___file___), 'templates').replace('\\','/'),
)
os.path.dirname(__file__)会获取setting.py 所在的目录,然后这个目录与templates连接。我们可以使用render_to_response()方法进一步简化代码。注:file两侧是两个下划线。
<html>
<body>
It is now {{current_date}}.
</body>
</html>
3)、编写views.py 文件
from django.shortcuts import render_to_response
import datetime
def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_date.html',{'current_date': now})
4)、修改urls.py
from django.conf.urls.defaults import patterns, include, url
from mysite.views import current_datetime
urlpatterns = patterns('',
('^current_time/$', current_datetime),
)
2、使用locals()
def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_datetime.html', {'current_date': now})
使用后:
def current_datetime(request):
current_date = datetime.datetime.now()
return render_to_response('current_datetime.html', locals())
直接使用与模板中的变量相同的名字命名临时变量current_date,调用的时候使用locals,它囊括了函数执行到该时间点定义的所有变量。
3、get_template()中使用子目录
t = get_template('dateapp/current_datetime.html')
由于
render_to_response() 只是对
get_template() 的简单封装, 你可以对
render_to_response() 的第一个参数做相同处理:
return render_to_response('dateapp/current_datetime.html', {'current_date': now})
4、include模板标签
{% include 'nav.html' %}
{% include "nav.html" %}
{% include 'includes/nav.html' %}
包含了以变量 template_name 的值为名称的模板内容:
{% include template_name %}六、模板继承
<!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>
2、修改模板使用该基础模板
{% extends "base.html" %}
{% block title %}The current time{% endblock %}
{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}
修改hours_ahead.html
{% extends "base.html" %}
{% block title %}Future time{% endblock %}
{% block content %}
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
{% endblock %}
3、views.py
from django.shortcuts import render_to_response
import datetime
def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_date.html',{'current_date': now})
def hours_ahead(request, hours_offset):
try:
hours_offset = int(hours_offset)
except ValueError:
raise Http404()
next_time= datetime.datetime.now() + datetime.timedelta(hours=hours_offset)
return render_to_response('hours_ahead.html', locals())
你可以根据需要使用任意多的继承次数。 使用继承的一种常见方式是下面的三层法:
- 创建 base.html 模板,在其中定义站点的主要外观感受。 这些都是不常修改甚至从不修改的部分。
- 为网站的每个区域创建 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。这些模板对 base.html 进行拓展,并包含区域特定的风格与设计
- 为每种类型的页面创建独立的模板,例如论坛页面或者图片库。 这些模板拓展相应的区域模板。
以下是使用模板继承的一些诀窍:
- 如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。
- 一般来说,基础模板中的 {% block %} 标签越多越好。记住,子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越多越好。
- 如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。
- 如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
- 不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。 也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。
- {% extends %} 对所传入模板名称使用的加载方法和 get_template() 相同。 也就是说,会将模板名称被添加到 TEMPLATE_DIRS 设置之后。
- 多数情况下, {% extends %} 的参数应该是字符串,但是如果直到运行时方能确定父模板名,这个参数也可以是个变量。 这使得你能够实现一些很酷的动态功能。