一、Django模板在视图中使用
1.从小的demo开始入手:
上一节,我们使用模板系统,都是在命令行中使用,在实际开发中,我们往往用在视图里。先看一个简单的例子(在djangobook书中的例子,或之前学习笔记中的例子基础上修改)。
1 #coding=utf-8 2 from django.http import HttpResponse 3 import datetime 4 from django.template import Template,Context #记住导入 5 6 #原先的视图函数 7 #def current_datetime(request): #[2] 8 # now=datetime.datetime.now() 9 # html="<html><body>现在时刻:%s.</body></html>" %now 10 # return HttpResponse(html) 11 12 #现在使用django模板来修改该视图函数 13 def current_datetime(request): 14 now=datetime.datetime.now() 15 t=Template('<html><body>现在时刻是:{{current_date}}</body></html>') 16 c=Context({'current_date':now}) 17 html=t.render(c) 18 return HttpResponse(html)
但是模板仍然嵌套在Python代码里面,我们可以使用django强大的API来从硬盘中载入模板,从而减少调用模板和模板本身的冗余。
2.载入模板
载入模板的流程:
(1):建立模板目录和模板文件,建议在mysite目录下,新建一个templates目录,里面放置模板文件,如.html/.csv等
(2):设置settings.py中的TEMPLATE_DIRS,该设置告诉django的模板加载机制在哪里查找模板。
1 TEMPLATE_DIRS = ( 2 '/home/django/mysite/templates', 3 )
1 最好把时区、语言也设置下: 2 #TIME_ZONE = 'America/Chicago' 3 TIME_ZONE = 'CCT' #时区设置为 China Coastal Time 4 5 #LANGUAGE_CODE = 'en-us' 6 LANGUAGE_CODE = 'zh-cn' #语言设置为中文简体,admin后台显示语言及form表单验证信息提示使用
注意:
[1]:你可以指定任何目录,只要那个目录下的目录和模板对于你的Web服务器运行时的用户是可读的
如果你找不到一个放置模板的位置,我们推荐你在Django工程目录下创建一个templates目录
[2]:不要忘了模板目录最后的逗号,Python需要逗号来区分单元素元组和括号括起来的语句,如果你想避免这个错误,可以用列表来替代元组,单元素列表不需要结尾的逗号;元组比列表略微高效,所以我们推荐使用元组
[3]:使用绝对路径很简单,如果你想更灵活和松耦合,你可利用Django的settings文件是简单的Python代码
这点来动态构建TEMPLATE_DIRS内容,例如:
1 import os.path 2 3 TEMPLATE_DIRS = ( 4 os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'), 5 )
Python内部变量 __file__被自动设置为代码所在的Python模块文件名
3.修改视图函数
1 #coding=utf-8 2 from django.http import HttpResponse 3 import datetime 4 from django.template import Template,Context #记住导入 5 from django.template.loader import get_template #记得导入 6 7 #原先的视图函数 8 #def current_datetime(request): #[2] 9 # now=datetime.datetime.now() 10 # html="<html><body>现在时刻:%s.</body></html>" %now 11 # return HttpResponse(html) 12 13 #使用django模板来修改该视图函数 14 #def current_datetime(request): 15 # now=datetime.datetime.now() 16 # t=Template('<html><body>现在时刻是:{{current_date}}</body></html>') 17 # c=Context({'current_date':now}) 18 # html=t.render(c) 19 # return HttpResponse(html) 20 21 #现在使用加载模板来修改该视图函数 22 def current_datetime(request): 23 now=datetime.datetime.now() 24 t=get_template('current_datetime.html') 25 c=Context({'current_date':now}) 26 html=t.render(c) 27 return HttpResponse(html)
注意:
(1):使用了函数 django.template.loader.get_template().加载模板文件,该get_template()函数以模板名称为参数,在文件系统中找处模块的位置,打开文件并返回一个编译好的Template对象
4.在模板目录下创建模块文件
1 <html><body>It is now {{ current_date }}.</body></html>
创建了current_datetime.html文件,并将视图函数中的now,填充模板变量{{current_date}}.
在我们所有配置完成之后(如:TEMPLATE_DIRS、TIME_ZONE = 'CCT' 、LANGUAGE_CODE = 'zh-cn')等,在浏览器中打开,我们可以看到解析后的页面显示如下:
二、Django高效的工作
1.render_to_response() 优化:
Django提供了一个捷径来使用一行代码完成载入模板,填充Context,渲染模板,返回HttpResponse对象的工作这就是render_to_response(),它在django.shortcuts模块下大部分情况下,你都会使用render_to_response()而不是手动完成上述的事情
1 from django.shortcuts import render_to_response #import 2 import datetime 3 4 #使用render_to_response优化处理 5 def current_datetime(request): 6 now=datetime.datetime.now() 7 return render_to_response('current_datetime.html',{"current_date":now})
(1):我们不在import get_template,Template,Context或者HttpResponse相反,我们import django.shortcuts.render_to_response,import datetime仍然存在.
(2):render_to_response()的第一个参数应该是使用的模板名,对应到模板目录的相对路径,第二个参数如果有的话应该是一个用来创建Context的字典,如果不提供第二个参数,render_to_response()将使用一个空的字典。
(3):render_to_response返回HttpResponse对象,所以我们return之。
2.locals()小技巧
像上面的,计算一些值后存储在变量中(例如now)并传递给模板懒程序员可能会觉得有点繁琐,既要给临时变量取名又要给模板变量取名这不仅仅是冗余,这是过度输入如果你很懒或者你想保持代码整洁,使用Python内建的locals()方法
locals()返回一个包含当前作用域里面的所有变量和它们的值的字典,上面的代码可以重写:
1 def current_datetime(request): 2 # now=datetime.datetime.now() 3 current_date=datetime.datetime.now() 4 return render_to_response('current_datetime.html',locals())
这里我们传递locals()的值而不是手动指定context字典,locals()包含了所有定义在当前方法的变量而且,我们把now变量重命名为current_date,因为模板需要的是这个变量名这个例子中locals()不会给你太大改善,但这个技术可以帮你少敲键盘。
使用locals()需要注意的是它包含了所有当前变量,可能包括比你的模板想访问的更多的变量上面的例子中,locals()也包括request变量,这依赖于你的程序。
最后要注意的是locals()导致了一点点开销,因为Python不得不动态创建字典,如果你手动指定context字典则可以避免这项开销。
3.get_template()中使用子目录
把所有模板放在一个目录下可能会让事情变得难以掌握,推荐把一些模板放在模板目录下的子目录里,如果我们方法,则只需要调用get_template()时,在模板名前面添加子目录名和斜线即可,如下:
1 def current_datetime(request): 2 current_date=datetime.datetime.now() 3 # t=get_template("date/current_datetime.html") 4 return render_to_response('date/current_datetime.html',locals())
注意:
(1):由于render_to_response()只是对get_template()的简单封装,可以对render_to_response的第一个参数做同样的处理。
(2):对于子目录树的深度没有限制
4.include模板标签
我们已经学习了模板载入机制,我们要介绍一个利用这个机制的内建标签:{% include %}
这个标签允许你引入另一个模板的内容,标签的参数是你想引入的模板的名字,名字可以是变量,也可以是单引号或双引号表示的string。
单双引号都可:
1 {% include 'nav.html' %} 2 {% include "nav.html" %}
下面的例子引入了includes/nav.html模板:
1 {% include 'includes/nav.html' %}
下面的例子引入了一个名字存在于template_name变量中的模板:
1 {% include template_name %}
和get_template()一样,请求的模板名前面会加上TEMPLATE_DIRS,在settings.py 中之前已经设置了:
1 TEMPLATE_DIRS = ( 2 os.path.join(os.path.dirname(__file__),'templates').replace('\\','/'), 3 )
则,include引用模板必须在该目录下,如templates目录下,这里我们引入hello.html
1 <h3> Is is now {{current_date}}</h3> 2 <h2 style="color:#F00">{%include "hello.html"%}</h2>
hello.html仅仅是一段字符串,如下:
1 <p>aaa</p>
则,在浏览器中输出:
注意:
如果被引入的模板中包含任何的模板代码,如标签和变量等,它将用父模板的context计算它们
如果给定的模板名不存在,Django将做下面两件事情中的一件:
(1):如果DEBUG设置为True,你将看到一个TemplateDoesNotExist异常的错误页面
(2):如果DEBUG设置为False,标签将什么也不显示
三、模板的继承
我们在使用include的时候,发现很有局限性,Django的模板继承很好地解决了这个问题。流程如下:
1.第一步是建立基本模板(如base.html),即你的子模板的框架
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title>{%block title %}{%endblock%}</title> 6 </head> 7 8 <body> 9 <h1>Django--BeginMan</h1> 10 {%block content%}{%endblock%} 11 {%block footer%} 12 <hr /> 13 <p style='color:#ccc'>Thanks for visiting my site</p> 14 {%endblock%} 15 </body> 16 </html>
这是一个主模板(基础模板),其他子模板的作用就是重载、继承、添加那些块的内容,{%block%}告诉模板引擎。子模块可以重载这部分。
2.第二步建立子模板
1 ----对current_datetime.html进行修改: 2 <body> 3 {%extends "base.html"%} 4 {%block title%}当前时间{% endblock %} 5 {%block content%} 6 <h3> Is is now {{current_date}}</h3> 7 8 {%endblock%} 9 </body> 10 </html>
1 --------对hello.html进行修改 2 <body> 3 {%extends "base.html"%} <!--第一个模板标记,即以它开头,否则模板继承将不起作用--> 4 {%block title%}HelloWorld{%endblock%} 5 {%block content%} 6 aaa 7 {%endblock%} 8 </body>
在浏览器中显示效果如下:
[current_datetime.html]
[hello.html]
注意:
(1):{%extends %} 不需要关闭标签,它只是单个出现。当出现{%extends "base.html"%}时,模板引擎会立即装载其父模板。
(2):{%block%}是成对出现的,使用过程中要以{%endblock%}关闭,当遇到{%block%}的时候,用子模板的内容替换这些block。
(3):可使用任意等级的继承,使用继承的常用方式是按以下三个步骤:
[1]:创建base.html模板来掌控你的网站的整体外观,它的内容很少改变
[2]:为你的网站创建base_SECTION.html模板,例如,base_photos.html,base_forum.html这些模板继承base.html并且包括部分专有的风格和设计
[3]:为每个类别的页面创建单独的模板,例如论坛页面或照片图库页面,这些模板拓展相应的模板区域
3、关于模板继承的小提示:
1,如果在模板里使用{% extends %}的话,这个标签必须在所有模板标签的最前面,否则模板继承不工作
2,通常基本模板里的{% block %}越多越好,子模板不必定义所有的父block,钩子越多越好
3,如果你在很多模板里复制代码,很可能你应该把这些代码移动到父模板里
4,如果你需要得到父模板的块内容,{{ block.super }}变量可以帮你完成工作
当你需要给父块添加内容而不是取代它的时候这就很有用
5,不能在同一模板里定义多个同名的{% block %},因为块标签同时在两个地方工作,不仅仅
在子模板中,而且在父模板中也填充内容,如果子模板有两个同名的标签,父模板将不能决定
使用哪个块内容来使用
6,你给{% extends %}传递的模板名同样会被get_template()使用,所以会加上TEMPLATE_DIRS设置
7,大部分情况下,{% extends %}的参数是string,但是也可以是变量,如果知道运行时才知道
父模板的名字,这可以帮助你做一些很cool的动态内容