Django模板引擎的研究
原创博文,转载请注明出处。
以前曾遇到过错误Reverse for ‘*’ with arguments '()' and keyword arguments' not found.1其原因是没有给视图函数传入参数。解决方法传入参数即可。
可是今天又遇到了同样的问题,发现原来的方法不好使了。研究了一下午,我发现原来在我的模板文件中,有多个视图函数需要传入同一名称的参数。如果不能明白我所表达的意思可以见下图
可以看到在blog.urls 所对应的视图函数必定需要一个参数realid,这就带来了多个视图函数需要传入同一名称的参数的问题。
问题的解决是我无意间把context_instance=RequestContext(request)去掉换成locals()。
所以,我决定把模板引擎进行一番学习。出自Django book。
1、基本知识:模板是一个文本,用于分离文档的表现形式和内容。模板定义了占位符以及各种用于规范文档该如何显示的各部分基本逻辑(模板标签)。模板通常用于产生HTML,但是Django的模板也能产生任何基于文本格式的文档。我们通常将模板和视图一起使用(实际,不限于此)。
2、最简单的模板系统实现方法:
(1)创建一个Template对象,Django支持用知道模板文件路径的方式来创建Template对象。
(2)模板渲染:调用模板对象的render方法,并且传入一套变量context。它将返回一个基于模板的展示字符串,模板中的变量和标签会被context指替换。
context是一系列变量和它们值得集合。
总结起来就是:写模板,创建Template对象,创建Context,调用render()方法。示例如下:
1 >>> from django.template import Template, Context
2 >>> raw_template = """<p>Dear {{ person_name }},</p>
3 ...
4 ... <p>Thanks for ordering {{ product }} from {{ company }}. It's scheduled
5 ... to ship on {{ ship_date|date:"F j, Y" }}.</p>
6 ...
7 ... {% if ordered_warranty %}
8 ... <p>Your warranty information will be included in the packaging.</p>
9 ... {% endif %}
10 ...
11 ... <p>Sincerely,<br />{{ company }}</p>"""
12 >>> t = Template(raw_template) 实例化Template对象
13 >>> import datetime
14 >>> c = Context({'person_name': 'John Smith', 实例化context对象
15 ... 'product': 'Super Lawn Mower',
16 ... 'company': 'Outdoor Equipment',
17 ... 'ship_date': datetime.date(2009, 4, 2),
18 ... 'ordered_warranty': True})
19 >>> t.render(c) 调用render方法
20 "<p>Dear John Smith,</p>\n\n<p>Thanks for ordering Super Lawn Mower from
21 Outdoor Equipment. It's scheduled \nto ship on April 2, 2009.</p>\n\n\n
22 <p>Your warranty information will be included in the packaging.</p>\n\n\n
23 <p>Sincerely,<br />Outdoor Equipment</p>"
django.template.Template类构造函数接受一个参数,原始模板代码。
django.template.Context类的构造函数带有一个可选的参数:一个字典变量和它们的值。
可以看到输出中,模板对象中的‘person_name’被替换成了‘john Smith’,‘product’被替换成了‘Super Lawn Mower’等等。
3、强大的模板加载:可以从磁盘中加载模板,也就是说我们实现了视图函数和模板文件的分离。让它使用 Django 模板加载功能而不是对模板路径硬编码。
首先我们必须设置settings.py里面的TEMPLATE_DIRS,将模板的保存位置告诉框架。这里我们介绍一种使用相对路径的方法
import os.path TEMPLATE_DIRS = (
os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
)
os.path.dirname(__file__)将会获取自身所在的文件,即settings.py 所在目录,然后由os.path.join这个方法将这目录与templates进行连接。前提是我们需要创建一个templates文件夹来存放模板文件
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)
此范例中,我们使用了函数 django.template.loader.get_template() ,而不是手动从文件系统加载模板。该 get_template() 函数以模板名称为参数,在文件系统中找出模块的位置,打开文件并返回一个编译好的 Template 对象。如果 get_template() 找不到给定名称的模板,将会引发一个 TemplateDoesNotExist 异常。
render_to_response()
由于加载模板、填充 context 、将经解析的模板结果返回为 HttpResponse 对象这一系列操作实在太常用了,Django 提供了一条仅用一行代码就完成所有这些工作的捷径。该捷径就是位于 django.shortcuts 模块中名为 render_to_response() 的函数。大多数时候,你将使用 render_to_response() ,而不是手动加载模板、创建 Context 和 HttpResponse 对象。
from django.shortcuts import render_to_response
import datetime def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_datetime.html', {'current_date': now})
render_to_response() 的第一个参数必须是要使用的模板名称。如果要给定第二个参数,那么该参数必须是为该模板创建Context 时所使用的字典。如果不提供第二个参数, render_to_response() 使用一个空字典。
Python 的内建函数 locals() 。它返回的字典对所有局部变量的名称与值进行映射。这可给你省了不少事情,但前提必须是变量的名称和模板文件被替换的值必须一致。因此,我们将 now 变量重命名为 current_date 。
def current_datetime(request):
current_date = datetime.datetime.now()
return render_to_response('current_datetime.html', locals())
同时,locals() 还包含了 request 。
RequestContext和Context处理器
RequestContext 默认地在模板context中加入了一些变量,如 HttpRequest 对象或当前登录用户的相关信息。
当你不想在一系例模板中都明确指定一些相同的变量时,你应该使用 RequestContext 。Context处理器允许你设置一些变量,它们会在每个context中自动被设置好,而不必每次调用 render_to_response() 时都指定。要点就是,当你渲染模板时,你要用 RequestContext 而不是 Context 。
建议选择 render_to_response() 作为context的处理器。像这样,使用 context_instance 参数,调用processors:
from django.shortcuts import render_to_response
from django.template import RequestContext def custom_proc(request):
"A context processor that provides 'app', 'user' and 'ip_address'."
return {
'app': 'My app',
'user': request.user,
'ip_address': request.META['REMOTE_ADDR']
} def view_1(request):
# ...
return render_to_response('template1.html',
{'message': 'I am view 1.'},
context_instance=RequestContext(request, processors=[custom_proc])) def view_2(request):
# ...
return render_to_response('template2.html',
{'message': 'I am the second view.'},
context_instance=RequestContext(request, processors=[custom_proc])) def view_3(request):
# ...
return render_to_response('template3.html',
{'message': 'I am the third view.'},
context_instance=RequestContext(request, processors=[custom_proc])) def view_4(request):
# ...
return render_to_response('template4.html',
{'message': 'I am the fourth view.'},
context_instance=RequestContext(request, processors=[custom_proc]))
同时,Django因此提供对 全局 context处理器的支持。Django提供了几个简单的context处理器,我们只需要在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 book
回到我所遇到的问题,由于我的视图函数中的,有的使用了RequestContext 有的使用了locals(),返回的参数各不相同,导致视图函数无法得到realid值,也许表达不够清晰。举个例子,我的视图函数中使用了context_instance=RequestContext(request),但是我却令模板中的<a href={% url addarticle realid=request.user.id %}> 参数realid值为request.user.id,这就出现了错误,当你添加了locals()后就可以使用了,因为locals()返回request参数。
所以,为了方便,我把所有的视图函数加载模板的部分都改成了这个形式:
return render_to_response("**.html",locals(),context_instance=RequestContext(request))