模板是一个文本,用于分离文本文件的表现形式和内容。模板通过占位符和包含逻辑的模板标签来填充动态内容。
用 {{ variable }} 括起来的文本称为“变量(variable)”,可以在此处插入指定的变量值
用{% expression %} 括起来的称为“模板标签(template tag)”,功能为通知模板解析系统完成某些逻辑功能。
常用的标签包含有:
{% if condition%}......{% else %}......{% endif %}
{% for %}......{% endfor %}
{% for ... reversed %}......{% endfor %}    反向迭代
{% for %}......{% empty %}......{% endfor %}    当迭代对象为空时
{% ifequal val1 val2 %}......{% else %}......{% endifequal %}    "ifequal"仅支持模板变量、字符串和数字,不支持布尔类型和其他集合类型
{# 这里添加注释 #}
{% comment %}......{% endcomment %}    多行注释
每个“{% for %}”循环里都有一个“forloop”模板变量。
“forloop.counter”值为循环当前进度,从 1 开始计数;
“forloop.revcounter”其值为剩余项的数量,总总数开始计数,最后一次循环其值为 1 ;
“forloop.first”是一个布尔值,仅在当前是第一项时为真;
“forloop.last”同上,仅在最后一项时为真。
使用“first”和“last”编写模板能使逻辑更加清晰,“last”的一个应用方向是在元素间添加管道符或逗号:
{% for link in links%}{{link}}{% if not forloop.last %} | {% endif %}{% endfor %}
效果: Link1 | Link2 | Link3 | Link4
标签中不能使用括号建立组合表达式,应使用嵌套或外部实现。
模板过滤器是变量在显示前修改他的值的一个简单方法,过滤器使用管道符,可以被套接,还可以带参数:
{{ var | innerfilter | outerfilter:"para" }}    参数要使用双引号
几个过滤器:
addslashes:添加反斜杠到任何反斜杠、单引号或者双引号前面
date:按指定的格式字符串参数格式化 date 或 datetime 对象,例:{{ pub_date|date:"F j, Y" }}
length:返回变量的长度
context在Django里表现为 Context 类,在 django.template 模块里。 她的构造函数带有一个可选的参数: 一个字典。 调用 Template 对象 的 render() 方法并传递context来渲染模板:
>>>t = template.Template("This is a template:{{name}}")>>>c = template.context({'name':'reciple'})>>>print(t.render(c)) This is a template:reciple
变量的形式还可以是高级数据结构,这时使用"."来访问子元素。包括字典、列表和对象的属性,甚至某些方法(如 upper 等方法,调用时不能加括号,所以也不能传参数)
>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')>>> t = Template('Item 2 is {{ items.2 }}.')
注意:模板中类列表对象不允许使用负索引(不应该把过于复杂的逻辑写在模板里,而应写到视图函数里)
对于某些永远不希望被模板调用的变量的方法(如存在安全风险),应当在类定义里给方法添加 .alters_data = True 属性,这样渲染模板时方法将不被执行:
def delete(self):# Delete the accountdelete.alters_data = True
单独保存的模板文件只需在视图函数中加载并渲染即可,关于保存模板的位置,最好配合 django 的模板加载 api 来选择:
在视图中加载模板,比如 get_template() 函数。它的默认行为是顺序使用以下加载器去寻找模板,并返回最先找到的模板:
TEMPLATE_LOADERS = ('django.template.loaders.filesystem.Loader','django.template.loaders.app_directories.Loader','django.template.loaders.eggs.Loader','django.template.loaders.cached.Loader', )
默认情况下 settings.py 并没有配置 TEMPLATE_LOADERS,这时前两个加载器是启用的,后两个是关闭的。第一个加载器 filesystem.Loader 的搜索依赖于 TEMPLATE_DIRS,而 1.6 版本的 settings 里面也默认没有配置它,所以除非手动添加 TEMPLATE_DIRS,否则 filesystem.Loader 什么也不会做。官方建议将 templates 文件夹放在项目目录,即与 app 同级,并通过 app 名来划分子路径。即:
#settingsBASE_DIR = os.path.dirname(os.path.dirname(__file__)) TEMPLATE_DIRS = ( os.path.join(BASE_DIR,'templates'), )
#viewsget_template('app\template_name.html')
如果不想去配置 TEMPLATE_DIRS 而是指望第二个加载器能够找到模板,那么就要把 templates 文件夹放在一个已安装的 app 目录下,因为 app_directories.Loader 的搜索路径中包含 app 目录。那么在这种情况下,我们可以在每个 app 目录下都建一个 templates 文件夹并存放这个 app 使用的模板吗?不建议这样做,因为这又牵扯到通用视图的搜索路径。即,通用视图的默认搜索路径就是“app\template_name”这种形式。所以在一个项目里,最好只有一个 templates 文件夹。
模板的具体加载路径,可以通过 TemplateDoesNotExist 异常的调试信息来查看:(下例中已经配置了 TEMPLATES_DIRS 指向根目录的 templates,另外,这是一个 ListView 的通用视图,‘books\publisher_list’这个名字是他自己定的)
********************************************************
Django tried loading these templates, in this order:
Using loader django.template.loaders.filesystem.Loader
:
C:\Users\July\Desktop\mysite\templates\books\publisher_list.html
(File does not exist)
Using loader django.template.loaders.app_directories.Loader
:
C:\WinPython-64bit-3.3.2.3\python-3.3.2.amd64\lib\site-packages\django\contrib\admin\templates\books\publisher_list.html
(File does not exist)
C:\WinPython-64bit-3.3.2.3\python-3.3.2.amd64\lib\site-packages\django\contrib\auth\templates\books\publisher_list.html
(File does not exist)
C:\Users\July\Desktop\mysite\books\templates\books\publisher_list.html
(File does not exist)
C:\WinPython-64bit-3.3.2.3\python-3.3.2.amd64\lib\site-packages\django\contrib\admin\templates\books\publisher_list.html
(File does not exist)
********************************************************
另外在视图中使用模板更便捷的做法其实是使用内建的函数一次性完成载入、渲染并返回 HttpResponse 的工作:
from django.shortcuts import render_to_response . ..return render_to_response('app\contact_form.html', {'form': form})
render_to_response() 的第一个参数必须是要使用的模板名称。 如果要给定第二个参数,那么该参数必须是为该模板创建 Context 时所使用的字典。 如果不提供第二个参数, render_to_response() 使用一个空字典。
对 render_to_response() 的第二个参数,一个偷懒的办法是使用 locals() 方法,这个 Python 的内建方法返回的是当前作用域内的所有局部变量的键值对字典。
模板可以继承,使用模板继承时,先定义一个基础模板,这个模板里的 {% block block_name %}......{% endblock %} 都可以被子模板覆盖;子模板的语法为:开头一句 {% extends 'base.html' %},下面开始覆盖 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_time.html:
{% extends "base.html" %} {% block title %}The current time{% endblock %} {% block content %}<p>It is now {{ current_date }}.</p>{% endblock %}
一种常见的模板继承方式是如下的三层法:
创建 base.html 模板。这是全站最外层的视图,几乎从来不改
为网站的每个区域创建 base_SECTION.html
为特定类型的页面创建独立的模板
django.template.RequestContext 对象与 Context 对象类似,不过其中包含了一些额外的 request 变量,可以看做是 Context 的子类。这种预渲染是出于简化视图代码的目的。
对 RequestContext 对象的预渲染操作是通过 Context 处理器函数实现的。这一过程发生在你调用 RequestContext 以取代 Context 的时候。渲染一个 RequestContext 的基本代码为:
c = RequestContext(request, {'message': 'I am view 1.'}, processors=[custom_proc])
RequestContext 的构造器接受三个参数,比 Context 多出来的两个分别为第一参数:request 对象 和 第三参数:Context 处理器列表。这里使用一个列表(或元组)是为了可以调用多个处理器函数。
在使用 render_to_response() 函数的情形下,渲染 RequestContext 的代码为:
return render_to_response('template.html',{'message': 'This is a RequestContext.'}, context_instance=RequestContext(request, processors=[custom_proc]))
与使用 Context 时的 render_to_response() 没有太大不同。
最后,上面两种在渲染时显式指定 Context 处理器的方法其实都不是最终方案,最常用的方法是使用全局 Context 处理器。这是一个配置于 django 安装目录下的 django/conf/global_settings.py 文件内的变量:
TEMPLATE_CONTEXT_PROCESSORS = ('django.contrib.auth.context_processors.auth','django.core.context_processors.debug','django.core.context_processors.i18n','django.core.context_processors.media','django.core.context_processors.static','django.core.context_processors.tz',# 'django.core.context_processors.request', 'django.contrib.messages.context_processors.messages', )
TEMPLATE_CONTEXT_PROCESSORS 内的这些处理器都会被默认调用,预渲染 RequestContext 对象。其中 ‘django.core.context_processors.request‘项是默认不启用的。当启用的时候,RequestContext 对象里就有了 request 对象。可以像下面这样获得客户端的 ip:
{{ request.REMOTE_ADDR }}
其他默认处理器提供的变量主要有:
user:一个 django.contrib.auth.models.User 实例,描述了当前登录用户(或者一个 AnonymousUser 实例,如果客户端没有登录)。
message:一个当前登录用户的消息列表(字符串)。 在后台,对每一个请求,这个变量都调用request.user.get_and_delete_messages() 方法。 这个方法收集用户的消息然后把它们从数据库中删除。
perms:django.core.context_processors.PermWrapper 的一个实例,包含了当前登录用户有哪些权限。
debug:你设置的 DEBUG 的值( True 或 False )。你可以在模板里面用这个变量测试是否处在debug模式下。
sql_quries:包含类似于 [ {'sql': ..., 'time': ...} , {'sql': ..., 'time': ...} , ... ] 的字典的一个列表, 记录了这个请求期间的每个SQL查询以及查询所耗费的时间。 这个列表是按照请求顺序进行排列的。
LANGUAGES:LANGUAGES 选项的值。
LANGUAGE_CODE:如果 request.LANGUAGE_CODE 存在,就等于它;否则,等同于 LANGUAGE_CODE 设置。
为了避免模板变量可能包含的 html 代码被客户端解释,在渲染模板的时候,django 会对变量进行自动转义。如果对某些变量可以信任,需要关闭转义功能,可以这么做:
This will be escaped: {{ data }} This will not be escaped: {{ data|safe }}
为了控制块级模板的自动转义,用标签autoescape来包装整个模板(或者模板中的一部分),就像这样:
{% autoescape off %} Hello {{ name }} {% endautoescape %}
autoescape 标签有两个参数on和off 。on 可以嵌套在 off 里面使用,以对一大块关闭自动转义的模板中的一小块再打开自动转义:
{% autoescape off %} This will not be auto-escaped: {{ data }}. {% autoescape on %} Auto-escaping applies again: {{ name }} {% endautoescape %} {% endautoescape %}
auto-escaping 标签的作用域不仅可以影响到当前模板还可以通过include标签作用到其他标签,就像block标签一样。 例如:
# base.html {% autoescape off %}<h1>{% block title %}{% endblock %}</h1>{% block content %} {% endblock %} {% endautoescape %} # child.html {% extends "base.html" %} {% block title %}This & that{% endblock %} {% block content %}{{ greeting }}{% endblock %}
由于在base模板中自动转意被关闭,所以在child模板中自动转意也会关闭.
一般来说不必过于在意自动转义系统,保持其打开状态,并在编写模板时适时使用 {% autoescape off %} 标签即可。
当编写模板的时候,如果不确认部署环境的自动转义功能是否被打开,可以对每一个确认需要转义的变量使用 escape 过滤器。它的功能正好与 safe 过滤器相反,而且即使自动转义被打开也不用担心会造成双重转义。
考虑这样一个模板变量:
{{ data|default:"This is a string literal." }}
当过滤器的参数里含有字符串的时候,这个字符串是不会被自动转义的,这是因为编写这个字符串的过程完全受作者的控制,也就是说对于特殊字符,需要这样写:
{{ data|default:"3 < 2" }} #Good {{ data|default:"3 < 2" }} #Bad
如果对此不习惯的话,则可以使用过滤器嵌套功能,比如上面的例子可以这样写:
{{ data|default:"3 < 2"|escape }} #Still good
除了 django.template.loader.get_template(template_name) 外,还有一个 django.template.loader.select_template(template_name_list) 函数可以用来在视图中加载模板。这个函数的特别之处在于,他接受一个包含了多个模板名的列表作为参数,并尝试顺序加载模板,加载成功后即返回。如果整个列表里的模板都无法加载,就引发一个 TemplateDoesNotExist 异常。
略...