上一篇Django模板-在视图中使用模板最后的问题,我们需要把数据和展现分离开。

你可能首先考虑把模板保存在文件系统的某个位置并用 Python 内建的文件操作函数来读取文件内容。 假设文件保存在 E:\djangosite\mysite\mysite\templates\tempTime.html 中的话,代码就会像下面这样:

<html><body>From tempTime.html====time now is {{current_date}}</body></html>
 def temp_time(request):
now= datetime.datetime.now()
#f = open(BASE_DIR + '\\mysite\\templates\\tempTime.html')
f=open(r'E:\djangosite\mysite\mysite\templates\tempTime.html')
content = f.read()
f.close()
t=Template(content)
c=Context({'current_date':now})
html = t.render(c)
return HttpResponse(html)

时刻记得在urls.py中去加配置哦

url(r'^temptime/$',temp_time),

运行效果如下:

基于以下几个原因,该方法还算不上简洁:

  • 它没有对文件丢失的情况做出处理。 如果文件 tempTime.html 不存在或者不可读, open() 函数调用将会引发 IOError 异常。

  • 这里对模板文件的位置进行了硬编码。

  • 它包含了大量令人生厌的重复代码。 与其在每次加载模板时都调用 open() 、 fp.read() 和 fp.close() ,还不如做出更佳选择。

为了解决这些问题,我们采用了 模板自加载 跟 模板目录 的技巧。

模板加载

为了减少模板加载调用过程及模板本身的冗余代码,Django 提供了一种使用方便且功能强大的 API,用于从磁盘中加载模板,要使用此模板加载API,首先你必须将模板的保存位置告诉框架。 打开settings.py配置文件,找到TEMPLATE_DIRS这项设置。(我本地新建的站点中,settings.py没有该项设置,但是官方文档中确实有它的介绍,自行加上即可)

这里有相关介绍,截图如下:

我新建的TEMPLATE_DIRS如下

import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
#这中间省去了其他配置项和说明
TEMPLATE_DIRS=(
os.path.join(BASE_DIR,'mysite\\templates').replace('\\','/'),
#r"E:\djangosite\mysite\mysite\templates".replace('\\','/'),
)

如果使用的是 Windows 平台,请包含驱动器符号并使用Unix风格的斜杠(/)而不是反斜杠(),就像下面这样

TEMPLATE_DIRS = (
'C:/www/django/templates',
)

BASE_DIR:Python 内部变量 __file__ ,该变量被自动设置为代码所在的 Python 模块文件名。os.path.dirname(__file__)将会获取自身所在的文件,即settings.py 所在的目录

完成 TEMPLATE_DIRS 设置后,下一步就是修改视图代码,让它使用 Django 模板加载功能而不是对模板路径硬编码

get_template方式

进行如下修改:区别就在注释那8和9两行

 import datetime
from django.template.loader import get_template
from django.template import Template,Context
from django.http import HttpResponse def temp_load(request):
now= datetime.datetime.now()
#t = get_template(BASE_DIR + '\\mysite\\templates\\tempTime.html')
t = get_template('tempTime.html')
c=Context({'current_date':now})
html = t.render(c)
return HttpResponse(html)

同样不要忘记去配置urls.py,这里就不多说了。

此范例中,我们使用了函数 django.template.loader.get_template() ,而不是手动从文件系统加载模板。 该get_template() 函数以模板名称为参数,在文件系统中找出模块的位置,打开文件并返回一个编译好的 Template对象。

我们选择的模板文件是tempTime.html,但这个与.html后缀没有直接的联系。 你可以选择任意后缀的任意文件,只要是符合逻辑的都行。甚至选择没有后缀的文件也不会有问题。

要确定某个模板文件在你的系统里的位置, get_template()方法会自动为你连接已经设置的 TEMPLATE_DIRS目录和你传入该方法的模板名称参数。

运行效果如下:

render_to_response方式

前面已经说明如何通过Template对象载入一个模板文件,然后用 Context渲染它,最后返回这个处理好的HttpResponse对象给用户。 之后优化方案,使用 get_template() 方法代替繁杂的用代码来处理模板及其路径的工作。 但这仍然需要一定量的时间来敲出这些简化的代码。 这是一个普遍存在的重复苦力劳动。Django为此提供了一个捷径,让你一次性地载入某个模板文件,渲染它,然后将此作为 HttpResponse返回。

该捷径就是位于 django.shortcuts 模块中名为 render_to_response() 的函数。

 from django.shortcuts import render_to_response
import datetime def temp_render(request):
now = datetime.datetime.now()
return render_to_response('tempTime.html', {'current_date': now})

render_to_response() 的第一个参数必须是要使用的模板名称。 如果要给定第二个参数,那么该参数必须是为该模板创建 Context 时所使用的字典。 如果不提供第二个参数, render_to_response() 使用一个空字典。

很多时候,我们要穿入到模板中的Context值很多,我们需要自己定义临时变量,然后组装Context对象,是不是觉得很麻烦?

Python内建函数 locals(),它返回的字典对所有局部变量的名称与值进行映射。 因此,前面的视图可以重写成下面这个样子:

 def temp_locals(request):
current_date = datetime.datetime.now()
return render_to_response('tempTime.html', locals())

我们只是将临时变量now变成了模板需要的变量名称current_date,然后传递到render_to_response的第二个参数不在我们去构造Context对象,直接使用locals()即可。

当遇到需要传递很多参数时,只需要定义对应名称的临时变量,通过locals即可一次性传递过去,着实方便很多。

还记得Django模板1最后说的吗,解决无效参数,即便locals传递冗余没用的或者少传递了必要参数,django也不会报错的,而是将该值复制为空。

render_to_response()是对get_template()简单封装,他们都可以传递子目录,类似于

t = get_template('dateapp/current_datetime.html')

需要注意的是 Windows用户必须使用斜杠(/)而不是反斜杠(\)

include 模板标签

在讲解了模板加载机制之后,我们再介绍一个利用该机制的内建模板标签: {% include %} 。该标签允许在(模板中)包含其它的模板的内容。 标签的参数是所要包含的模板名称,可以是一个变量,也可以是用单/双引号硬编码的字符串。 每当在多个模板中出现相同的代码时,就应该考虑是否要使用 {% include %} 来减少重复。

下面的例子包含了 includes/nav.html 模板的内容:

在templates下创建mypage.html和includes文件夹,文件夹下创建nav.html,如下

 # mypage.html

 <html>
<body>
{% include "includes/nav.html" %}
<h1>{{ title }}</h1>
</body>
</html>
 # includes/nav.html

 <div id="nav">
You are in: {{ current_section }}
</div>
 def temp_include(request):
now = datetime.datetime.now()
c= Context({'title':'my title','current_section':'mysection'})
return render_to_response('mypage.html',c)

运行效果

模板继承

使用include标签可以解决一定层面上的代码代码重用问题,但是考虑一下,include是怎么一个情况呢?

定义页面公共部分的代码分别放置在不同的文件中,某一个页面需要时便include进来。这样页面布局一致,展示内容不同的页面都要分别取做页面,并include不同的文件。

这里强调了页面布局一致。

由此想起面向对象的类继承,我们抽象一个一致的页面布局,然后让内容不同的子页面扩展该布局怎么样?就是说和include思想相逆的方式。

Django确实提供了这样一种方式---模板继承 。

本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含的公用部分和定义块进行重载。

定义基础模板,该框架之后将由 子模板 所继承。 以下是我们目前所讲述范例的基础模板:

 <!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>

这个叫做 base.html 的模板定义了一个简单的 HTML 框架文档,我们将在本站点的所有页面中使用。 子模板的作用就是重载、添加或保留那些块的内容。

所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。 每个{% block %}标签所要做的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。

现在我们已经有了一个基本模板,我们可以修改 current_datetime.html 模板来 使用它:

 {% extends "base.html" %}

 {% block title %}The current time{% endblock %}

 {% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}

工作方式

在加载 current_datetime.html 模板时,模板引擎发现了 {% extends %} 标签, 注意到该模板是一个子模板。 模板引擎立即装载其父模板,即本例中的 base.html 。

此时,模板引擎注意到 base.html 中的三个 {% block %} 标签,并用子模板的内容替换这些 block 。因此,引擎将会使用我们在 { block title %} 中定义的标题,对 {% block content %} 也是如此。

注意由于子模板并没有定义 footer 块,模板系统将使用在父模板中定义的值。 父模板 {% block %} 标签中的内容总是被当作一条退路。

继承并不会影响到模板的上下文。 换句话说,任何处在继承树上的模板都可以访问到你传到模板中的每一个模板变量。

使用模板继承的一些说明:

  • 如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。

  • 一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。

  • 如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。

  • 如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。

  • 不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。 也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。

  • {% extends %} 对所传入模板名称使用的加载方法和 get_template() 相同。 也就是说,会将模板名称被添加到TEMPLATE_DIRS 设置之后。

  • 多数情况下, {% extends %} 的参数应该是字符串,但是如果直到运行时方能确定父模板名,这个参数也可以是个变量。

{{ block.super }}示例

 {% extends "base.html" %}

 {% block title %}Block.surper {% endblock %}
{% block content %}
{{ block.super }}<br />
From current_datetime.html====time now is {{current_date}}
{% endblock %}

和上边current_datetime.html相比之增加了红色第五行代码

意思很明白了:子模板中,重写的{% block %}内包含{{ block.super }}就可以先展示父模板内容,再添加重写的内容。就像Java中super和c#中base。

小结

模板终于算是简单写完了,东西很多很杂乱,但是不难。大多数都是从DjangoBook摘过来的,如果写的不够清晰,请查看原文。

Django模板-分离的模板的更多相关文章

  1. Django学习日记04_模板_overview

    通过Django中的模板,使得设计人员和网站管理有一个对接的接口,实现网页设计和逻辑的分离,因此,模板会处理大量的文本解析内容,django中内部使用了高效的引擎来完成模板解析. 模板设置 在使用模板 ...

  2. Django学习系列之模板

    什么是django模板 模板是一个文本,用于分离文档的表现形式和内容,模板定义了占位符以及各种用于规范文档该如何显示的各部分基本逻辑(模板标签) 模板通常用于产生HTML 如何使用模板 创建一个Tem ...

  3. django基础知识之模板:

    模板介绍 作为Web框架,Django提供了模板,可以很便利的动态生成HTML 模版系统致力于表达外观,而不是程序逻辑 模板的设计实现了业务逻辑(view)与显示内容(template)的分离,一个视 ...

  4. Django(十五)模板详解:模板标签、过滤器、模板注释、模板继承、html转义

    一.模板的基础配置及使用 [参考]https://docs.djangoproject.com/zh-hans/3.0/topics/templates/ 作为Web框架,Django提供了模板,用于 ...

  5. Django(4)html模板继承、模板导入、分页实现

    1.获取所有请求信息 导入模块:from django.core.handlers.wsgi import WSGIRequest request.environ:包含所有的请求信息,可以打印看一下, ...

  6. C++模板分离

    在正常情况下,c++模板是不允许在头文件声明,在cpp文件中实现.那是因为在cpp文件在编译时内存必须要给它分配储存空间.但是模板本身是一种泛型,在没有明确定义声明类型前,编译器也无法知道它的大小.所 ...

  7. 在Django中使用Mako模板

    用了一个月后,终于忍受不了Django的模板了.主要原因是模板内不能运行原生的python语句,所以用起来总感觉被人绑住手脚,遍历个字典都要搞半天. 所以决定用第三方的模板.查了一下,django用的 ...

  8. Python第十三天 django 1.6 导入模板 定义数据模型 访问数据库 GET和POST方法 SimpleCMDB项目 urllib模块 urllib2模块 httplib模块 django和web服务器整合 wsgi模块 gunicorn模块

    Python第十三天   django 1.6   导入模板   定义数据模型   访问数据库   GET和POST方法    SimpleCMDB项目   urllib模块   urllib2模块 ...

  9. Django模板之通用模板的使用

    Django模板之通用模板的使用 转载:https://code.ziqiangxuetang.com/django/django-template.html 我们做网站有一些通用的部分,比如 导航, ...

随机推荐

  1. java poi 导出excel

    poi的jar下载地址:http://poi.apache.org/ 下载后会有很多jar,但是如果只是简单的excel报表的话,导入一个poi-版本号-日期.jar就可以了. 导出代码: priva ...

  2. avalon中常用的事件

     ms-on-change 相当于失去焦点事件. ms-on-input 相当于watch事件 http://www.runoob.com/jsref/event-oninput.html

  3. mybatis常用jdbcType数据类型

    MyBatis 通过包含的jdbcType类型 BIT         FLOAT      CHAR           TIMESTAMP       OTHER       UNDEFINED ...

  4. 使用jquery插件uploadify上传文件的方法与疑问

    我是学生一枚,专业也不是计算机,但又要用到很多相关技术,所以在技术基础不牢靠的情况下,硬着头皮在做.最近在做一个小项目需要上传图片,而且是需要用ajax的方式.但是利用jquery的ajax方法总会有 ...

  5. 如何使用composer?

    /** *@测试环境:我笔记本 本地xampp集成环境 *@操作系统:Windows 7 **/   安装方法:官方有很详细的介绍,这里就不重复造轮子了. 官方链接:http://docs.phpco ...

  6. python针对于mysql的增删改查

    无论是BS还是CS得项目,没有数据库是不行的. 本文是对python对mysql的操作的总结.适合有一定基础的开发者,最好是按部就班学习的人阅读.因为我认为人生不能永远都是从零开始,那简直就是灾难. ...

  7. class之cls

    cls代表的是类本身,一般在静态方法中使用

  8. SemaphoreFullException when checking user role via ASP.NET membership

    将指定的计数添加到该信号量中会导致其超过最大计数 This issue was fixed by restarting ASP.NET Development Server on windows ta ...

  9. MYSQL 的 3 类数据类型

    1.数据型: bool,float,double decimal(M,D) M是小数位数(精度)的总数,D是小数点(标度)后面的位数.DECIMAL整数最大位数(M)为65. smallint 小的整 ...

  10. JSON.parse()和JSON.stringify()的用法

    JSON.parse()是用于从一个字符串中解析出json对象,如下所示 var str = '{"name":"flsummer","age&quo ...