django在视图中获取Template
django在视图中获取Template
现在对Template进行深入了解
在试图中使用模板(了解)
视图代码:
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
用Django模板系统来修改视图。首先想到这样子
from django.template import Template, Context
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
t = Template("<html><body>It is now {{ current_date }}.</body></html>")
html = t.render(Context({'current_date': now}))
return HttpResponse(html)
它确实使用了模板系统,但是模板仍然嵌入在Python代码中,并未实现数据和表现的分离。我们现在将模板放置一个单独的文件,并让视图加载来解决该问题。
你可能考虑将模板保存在文件系统的某个位置并用Python内建的文件操作函数来读取文件内容。类似这样子:
from django.template import Template,Context
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
# Simple way of using templates from the filesystem.
# This is BAD because it doesn't account for missing files!
fp = open('/home/lyh/mycode/question_generate/question/templates/question/mytemplate.html')
t = Template(fp.read())
fp.close()
html = t.render(Context({'current_date':now})
return HttpResponse(html)
然而,基于以下几个原因,该方法还算不上简洁:
它没有对文件丢失的情况做出处理。 如果文件 mytemplate.html 不存在或者不可读, open() 函数调用将会引发 IOError 异常。
这里对模板文件的位置进行了硬编码。 如果你在每个视图函数都用该技术,就要不断复制这些模板的位置。 更不用说还要带来大量的输入工作!
它包含了大量令人生厌的重复代码。 与其在每次加载模板时都调用 open() 、 fp.read() 和 fp.close() ,还不如做出更佳选择。
为了解决这样的问题,我们采用模板自加载和*模板目录*的技巧
get_template(重点)
为了减少模板加载调用过程以及模板本身的冗余代码,Django提供了一种使用方便且功能强大的API,用于从磁盘中家在模板,
要使用次模板加载API,首先你必须将模板的保存位置告诉框架。设置的保存文件是ROOT_URLCONF配置的settings.py。
打开settings.py配置文件,找到TEMPLATE_DIRS这项设置,默认是一个空元祖(tuple)。
TEMPLATE_DIRS = (
# Put strings here, like "/home/django/django_templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.)
该设置告诉 Django 的模板加载机制在哪里查找模板。 选择一个目录用于存放模板并将其添加到 TEMPLATE_DIRS 中:
TEMPLATE_DIRS = (
'/home/lyh/mycode/mysite/polls/templates',
)
注意:
你可以任意指定想要的目录,只要运行 Web 服务器的用户可以读取该目录的子目录和模板文件。 如果实在想不出合适的位置来放置模板,我们建议在 Django 项目中创建一个 templates 目录(也就是说,如果你一直都按本书的范例操作的话,在第二章创建的 mysite 目录中)。
如果你的 TEMPLATE_DIRS只包含一个目录,别忘了在该目录后加上个逗号。
Python要求单价元素元组必须使用逗号,以此消除与圆括号表达式之间的歧义。
最省事的方法是使用绝对路径(即从文件系统根目录开始的目录途径)。但是如果你想要灵活点并减少负面干扰,可以用Django配置文件就是PYthon代码这一点来动态构建TEMPLATE_DIRS的内容,例如:
import os.path
TEMPLATE_DIRS = (
os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
)
这里使用了Python的内部变量__file__,该变量被自动为代码所在的Python模板文件名。‘os.path.dirname(file)’将获取自身所在的文件夹,即settings.py所在的目录,然后由os.path.join这个方法将此目录与templates进行链接。若在windows下,它会只能的选择正确的反斜杠进行链接,不是有正斜杠。
完成TEMPLATE_DIRS设置后,下一步修改视图代码,让它使用Django模板加载功能而不是对模板路径编码。返回current_datetime视图,如下修改:
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 对象。
在这个例子里,我们选择的模板文件是current_datetime.html,但这个与.html后缀没有直接的联系。 你可以选择任意后缀的任意文件,只要是符合逻辑的都行。甚至选择没有后缀的文件也不会有问题。
要确定某个模板文件在你的系统里的位置, get_template()方法会自动为你连接已经设置的 TEMPLATE_DIRS目录和你传入该法的模板名称参数。比如,你的 TEMPLATE_DIRS目录设置为 '/home/lyh/mycode/mysite/polls/templates',上面的 get_template()调用就会为你找到 /home/lyh/mycode/mysite/polls/templates/current_datetime.html 这样一个位置。
如果 get_template() 找不到给定名称的模板,将会引发一个 TemplateDoesNotExist 异常。 要了解究竟会发生什么,在 Django 项目目录中运行 python manage.py runserver 命令,再次启动Django开发服务器。 接着,告诉你的浏览器,使其定位到指定页面以激活current_datetime视图(如 http://127.0.0.1:8000/time/ )。假设你的 DEBUG项设置为 True,而你有没有建立current_datetime.html 这个模板文件,你会看到Django的错误提示网页,告诉你发生了 TemplateDoesNotExist 错误。
接下来,在模板目录中创建包括以下模板代码current_datetime.html文件:
<html><body>It is now {{ current_date }}.</body></html>
刷新网页,发现新大陆咯
render_to_response()
好,已经说了如何加载一个模板文件,然后用Context渲染它,之后返回这个处理好的HttpResponse对象给用户。我们已经采用了优化方案,使用get_template()来代替繁华用代码来处理模板及其路径的工作。但这仍然需要一定量的时间来敲代码。Django为此提供了一个捷径,让你一次性的载入某个模板文件,渲染它,然后将此作为HttpResponse返回
下面是使用render_to_response()重新编写过的current_datetime例子:
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})
(1)我们不在需要导入get_template、Template、Context和HttpResponse。相反,我们导入django.shortcuts.render_to_response。
(2)在current_datetime函数中,我们任然进行now计算,但模板加载,上下文创建、模板解析和HttpResponse创建工作均对render_to_response()的调用中完成了。由于render_to_response()返回HttpResponse对象,因此我们仅需在试图中return该值
render_to_response()的第一个参数必须是要使用的模板名称。如果要给定第二个参数,那么该参数必须是为该模板创建Context时所使用的字典。如果不提供第二个参数,render_to_response()使用一个空字典。
locals()技巧
很多时候,我们像上一个例子一行,发现自己一直在计算某一个变量,保存结果到变量中(比如前面代码中的now),然后将这些变量发送给模板。我们会发现,不断地为临时变量*和临时模板*命名有那么一点儿冗余,不仅多余,还需要额外的输入。
如果你是一个喜欢偷懒的程序员病想让代码看起来更加简明,可以利用Python的内建函数locals()。它返回的字典对多有局部变量的名称与值进行映射。因此,前面的试图可以写成下列样子:
def current_datetime(request):
current_date = datetime.datetime.now()
return render_to_response('current_datetime.html', locals())
我们没有像之前那样手工指定context字典,而是传入了locals()的值,它囊括了函数执行到该时间点时所定义的一切变量。因此,我们将now变量重命名为current_date,因为那才是模板所预期的变量名称。
使用locals()时要注意它将包括*所有*的局部变。
get_template()中使用子目录
把所有的模板都存放在一个目录下可能会让事情变得难以掌控。根据Miller法则,一个层级下的文件数量在7±2个为好。考虑把模板放在你模板目录的子目录中,这样会很好,而且也推荐这样做。
把模板放在模板目录的子目录中是件非常轻松的事情。只需要在调用get_template()是,把子目录名和一条斜杠添加到模板名称之前,for example:
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})