Skip to content Skip to main navigation Skip to footer

Python: Django笔记 —— 模板

最近在学习Django,打算玩玩网页后台方面的东西,因为一直很好奇但却没怎么接触过。Django对我来说是一个全新的内容,思路想来也是全新的,或许并不能写得很明白,所以大家就凑合着看吧~

本篇笔记(其实我的所有笔记都是),并不会过于详细的讲解。因此如果有大家看不明白的地方,欢迎在我正版博客下留言,有时间的时候我很愿意来这里与大家探讨问题。(当然,不能是简简单单就可以百度到的问题-.-)

我所选用的教材是《The Django Book 2.0》,本节是模板部分,对应书中第四章。

————————————————————————————————————————————————

0、代码示例

创建一个网站:django-admin.py startproject four

在网站根目录下创建一个文件夹:templates(网站根目录也就是那个有manage.py的目录,定位网站中任何文件,都默认此目录作为根目录)

在./templates/下,创建home.html,内容如下:

1  < html >
2  < head >
3  </ head >
4  < body >
5  Hello~I'm {{name}}^.^< br  />
6  </ body >
7  </ html >  

在./templates/下,创建home.txt,内容如下:

1  name    qiqi

在./four/下,修改urls.py,内容如下:

1  from  django.conf.urls import  url
 2  from  four.views import  home
 3
4  urlpatterns = [
 5      url(r' ^$ ' , home),
 6  ] 

在./four/下,创建views.py,内容如下:

 1  from  django.http import  HttpResponse
  2  from  django.template import  Template, Context
  3
 4  def  home(request):
  5      tin = open(' ./templates/home.html ' )
  6      html = tin.read()
  7      tin.close()
  8
 9      cin = open(' ./templates/home.txt ' )
 10      inf = {}
 11  for  line in  cin.readlines():
 12          a,b = line.split()
 13          inf[a] = b
 14      cin.close()
 15
16      t = Template(html)
 17      c = Context(inf)
 18  return  HttpResponse(t.render(c)) 

此时,我们运行服务器(python manage.py runserver),便可以访问到以下网页了:

网址 内容
http://127.0.0.1:8000/ Hello~I’m qiqi^.^

1、模板简介

这一章原文很长,我想,我先向大家大体介绍一下模板,后面再详述细节。

先说咱们熟悉的urls.py和views.py:

urls.py中删去了无用代码,只创建了一个home。

views.py中则利用模板实现了这个home。其中,除了Template、Context和render(即最后三行)属于模板内容之外,其余部分均为python中文件读取的语句。很显然,我把前面创建的home.html读到了html变量里,这是一个字符串;又把home.txt读到了inf变量里,这是一个字典。

好的,知道了这些,下面咱们开始介绍模板:

home.html就是一个模板,大家可以看到,这就是一段html代码,唯一不同的是,其中多了一个”{{name}}>”。这是模板里“变量”的写法,变量名是name。这样写出一段可以包含变量的html代码,就算是写出了一个最基础的模板。

而home.txt中的内容,显然就是把name赋值成qiqi。这个东西就是为模板填写的内容了。

最后我们在views.py中,用Template函数把html代码转换成 模板
,用Context函数把字典转换成 内容(书中有时翻译成“上下文”,但我更喜欢翻译成“内容”)
,而render函数则利用这个模板 渲染
了这段内容,生成一个网页。这就是传说中的模板了。

这里说明一个细节:render所产生的是Unicode字符串(大家可以自己在python交互界面下试试)!

看到这里,大家应该已经知道什么是模板,并且可以使用Django中的模板做一些有变量的网页了。但我更希望,大家能感到, 这过程非常简单,各位同学完全可以自己用任何语言来完成这个任务。因为暂时来看,模板无非就是段变量替换的代码而已,随便一个会编程的人都可以做的。

2、模板基础语法

模板当然不只有替换变量这一个东西,那么我们现在开始介绍模板的各种用法:

引用/语句 示例 解释 备注
变量 {{ name }} name是变量名,

如果这个变量不存在,模板中对应位置则会写成一个空字符串

(下面各种变量也都一样, 方法还没有实验!!!

字典变量

{{ person.name }}

{{ person.age }}

person是字典,

有name和age两个key

某个对象

{{ d.year }}

{{ d.month }}

{{ d.day }}

import datetime

d=datetime.date(2015, 5, 21)

自定义类

{{ person.name }}

{{ person.age }}

person是自己定义的类,

name和age是里面的成员变量

成员方法

{{ str.upper }}

{{ str.isdigit }}

str是string类型,

string类型有upper和isdigit函数

1. 只能调用不需传参数的方法。

2. 如果方法中出现了错误,按说应该报错。

但如果这个错误中有下面这个属性,则不会报错:

silent_variable_failure = True,

此时,模板中对应位置会被写成一个空字符串。

3. 模板系统不会执行任何以下列方式标记的函数,

即使调用了这个函数,也会默默退出,什么都不做:

设置函数属性alters_date = True。

列表索引

{{ items.0 }}

{{ items.1 }}

{{ items.2 }}

items=[‘go’,’phone’,’computer’]

不能使用负数列表索引:

例如{{ items.-1 }},会触发TemplateSyntexError。

if

语句

{% if value %} 

# aaa

{% else %}

# bbb

{% endif %}

value是变量名,

else是可选的

1. 变量为真,则会显示aaa处内容;否则,显示bbb处内容。

所谓真,即:变量存在、非空、非布尔值False。只有以下值为假:

[], (), {}, ”, 0, None, False, 自定义对象中定义布尔值属性为False。

2. 可以使用and,or,not进行逻辑运算,也可以用not对变量取反,

但一句if中不可以同时出现and和or,以免造成逻辑混乱。

注意,不可以使用括号来提示优先级,not优先级高于and和or。

3. 可嵌套(也可与for相互嵌套)

4. 不支持别的if语法(例如elif)

ifequal

语句

{% ifequal x y %}

# aaa

{% else %}

# bbb

{% endifequal %}

如果x与y两变量相等,

那么显示aaa的内容,

否则显示bbb的内容。

x与y只能是变量、字符串、整型常数和浮点型常数。

其它类型都不能用,例如:字典、列表、布尔。

因此,判断变量值的真假,应当使用if语句。

for

语句

{% for x in Y %} 

# …

{% empty %}

# …

{% endfor %}

Y是要循环(又称迭代)的序列

x是每次循环中使用的变量名称

empty是可选的,

是Y为空时所显示的代码

1. 可以使用reversed反向迭代列表

2. 在每个循环中都有一个模板变量,其中含有一些提示循环进度的属性

forloop.counter:当前循环次数,从1开始计数

forloop.counter0:当前循环次数,从0开始计数

forloop.revcounter:当前剩余循环次数,从N开始,最后为1

forloop.revcounter0:当前剩余循环次数,从N-1开始,最后为0

forloop.first:布尔值,仅第一次迭代时为True

forloop.last:布尔值,仅最后一次迭代时为True

forloop.parentloop:指向上一级循环的forloop对象的引用

如果我们自己定义了一个forloop变量,Django模板也不会报错,

但我并不想介绍其语法,只是希望大家记住变量别叫这个名字就好。

2. 可嵌套(也可与if相互嵌套)

3. 不支持别的for语法(例如break,continue)

过滤器

(filter)

{{ name|lower }}

{{ mylist|first|upper }}

{{ s|truncatewords:”30>” }}

利用Unix的管道符’|’表示过滤器

左面举了4个例子:

1. 把name小写输出

2. 把mylist中第一个字符大写输出

3. 把s前30个字符输出

这里仅仅介绍几种常用过滤器,全部的介绍参见本书的附录F。

0. 左面例子中那几种

1. addslashes,添加反斜杠到任何反斜杠、单引号、双引号前面。

这在处理包含JavaScript的文本时非常有用。

2. date,按指定的格式字符串参数格式化date或datetime对象。

例如:{{ now|date:”F j, Y>” }},含义详见附录。

3. length,返回变量的长度(针对有__len__()方法的对象)。

这里为新手说一句,列表、字符串就有__len__()方法,用来测长度。

单行注释 {# something #}  something是被注释的内容 
多行注释

{% comment %} 

# …

{% endcomment %}

2.1 ‘.’的优先级

大家可能已经注意到了,在Django的模板中,各种复杂变量都是使用一个点’.’来引用的。那么,这必然会有优先级问题:

查找顺序 类型 代码实例
1 字典 qiqi[“hello>”]
2 属性 qiqi.hello
3 方法 qiqi.hello()
4 列表索引 qiqi[0]

啰嗦一句,查找时采用的是“短路逻辑”,即找到一个匹配的就不继续往下找了。

2.2 ‘.’可以多级嵌套

2.3 报错信息

当遇到模板语法错误时,那么在调用Template函数时,会抛出TemplateSyntexError异常。所谓错误,有下列几种情况:

情况 备注
无效的标签(tags)
标签的参数无效
无效的过滤器(filter)
过滤器的参数无效
无效的模板语法
未封闭的块标签 显然是对需要封闭的块标签而言的

2.4 Context修改

Context是可以像字典一样添加、删除条目的 (但字典别的操作我就不知道Context有没有了!!!)

3、如何使用模板

开头的代码,的确已经比较简洁地用上了模板。但其中,读取文件的操作显然又是要不断重复书写的代码。因此,Django进一步做了优化。

3.1 模板加载(get_template函数)

打开settings.py,你会看到一个TEMPLATES,其中有一个DIRS列表,这便是Django查找模板的地址了。

因此,在此处加上你的模板路径,例如咱们的templates文件夹,即:’./templates/’。

然后,修改views.py如下(仔细看,代码不只函数内容变了,import处也改了两行):

 1  from  django.http import  HttpResponse
  2  from  django.template import  Context
  3  from  django.template.loader import  get_template
  4
 5  def  home(request):
  6      cin = open(' ./templates/home.txt ' )
  7      inf = {}
  8  for  line in  cin.readlines():
  9          a,b = line.split()
 10          inf[a] = b
 11      cin.close()
 12
13      t = get_template(' home.html ' )
 14      c = Context(inf)
 15  return  HttpResponse(t.render(c)) 

如此,你会发现网页已经正常运行,而你的代码简洁了不少;如果你的路径写错了,则会出现报错页面,告诉你发生了TemplateDoesNotExist错误。

get_template函数内的路径是可以有子目录的。

在此提醒一句,我的笔记只针对Linux用户,因为我用的就是Linux(在第一篇Django笔记中就已声明过了)。关于Windows用户路径是反斜杠的问题,请去查阅原书,其中有详细介绍。

3.2 模板加载并渲染(render_to_response函数)

我们代码还可以进一步简洁,如下:

 1  from  django.shortcuts import  render_to_response
  2
 3  def  home(request):
  4      cin = open(' ./templates/home.txt ' )
  5      inf = {}
  6  for  line in  cin.readlines():
  7          a,b = line.split()
  8          inf[a] = b
  9      cin.close()
 10
11  return  render_to_response(' home.html ' , inf) 

这个函数很简单,根据给定的字符串找到模板,再把给定的字典转换成内容,然后用模板渲染内容,最后生成Http response。

如果仅仅给出一个参数,字典没给,则默认是导入一个空字典。

render_to_response函数第一个参数的路径依旧可以有子目录,因为这个函数仅仅是对get_template函数的简单封装。

3.3 一个偷懒的小方法(locals函数)

前面用文件home.txt存变量,适合信息比较多的页面。那如果我们的页面中变量很少呢?

前面的做法适合多人协作,写网页的和写网站的分开。那如果我们是一个人做这两件事,而home.txt信息又很少,存个文件岂不是多此一举?

这时候,我们可以考虑使用python内建的函数locals(),这函数返回值中,包括了其所在函数从开头运行到现在所有的局部变量。

1  from  django.shortcuts import  render_to_response
 2
3  def  home(request):
 4      name = ' qiqi '
5  return  render_to_response(' home.html ' , locals()) 

代码的确很简单,但需要注意的是,locals()不只有name,还包含了request。代码简洁,实际却多传了变量。如何取舍呢?看你自己了。

4、模板包含(include模板标签)

代码 解释
{% include ‘sub.html’ %} 这是相对路径,路径起点就是上面搜索模板的路径
{% include “sub.html>” %} 除了单引号,双引号字符串也是支持的
{% include ‘includes/sub.html’ %} 可以有子目录
{% include template_name %} 也可以使用字符串变量

如果你的路径错误,那么Django会查看你settings.py中的DEBUG参数:

若参数为Ture,那么你会在Django的报错页面中看到TemplateDoesNotExist错误;

若参数为False,那么该标签不会引发错误,其位置上不显示任何东西。

这里提醒一点,假如你的home.html模板,包含了一个sub.html模板。然后你用home.html模板渲染了一段内容,这内容应当同时包含两个模板所需的变量,这样sub.html方能有你所想要的内容。

5、啰嗦两句(没兴趣的直接跳过)

有些同学很喜欢猜谜,喜欢猜出作者所想的共鸣感。那么我现在给出一句话,随着下面的学习,相信在我最终解释之前,你会提前明白这句话的: 模板继承与模板包含,思路是相反的。

但就我个人来说,非常反感猜谜。例如,很多书中都会先给出概念,然后隔很久才给出其实例(教材中屡见不鲜)。对这种做法,我简直是深恶痛绝。这会把我本来觉得很有意思的东西变得艰涩难懂,让我失去阅读的兴趣。因此,我向来都喜欢把文章尽我所能写得深入浅出,至少能让自己隔一段时间依旧很容易看懂。

又忍不住啰嗦了几句,没兴趣的读者请见谅,忽略这些就好……咱们回到正题,来说模板的继承。

6、模板继承代码

这段最好还是直接看代码比较好理解,下面让我们来重新建一个网站,名字叫FOUR。

首先按照咱们前面学的,把settings.py里面TEMPLATES参数的DIRS部分,加上一个路径:’./template/’。

然后,在网站根目录建一个文件夹template,里面写出三个模板,内容如下:

base.html

 1  <! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN>" >
 2  < html  lang ="en>" >
 3  < head >
 4  < title > {% block title %}{% endblock %}</ title >
 5  </ head >
 6  < body >
 7  < h1 > My helpful timestamp site</ h1 >
 8      {% block content %}{% endblock %}
  9      {% block footer %}
 10  < hr >
11  < p > Thanks for visiting my site.</ p >
12      {% endblock %}
 13  </ body >
14  </ html >  

current_datetime.html

1  {% extends "base.html>" %}
 2
3  {% block title %}The current time{% endblock %}
 4
5  {% block content %}
 6  < p > It is now {{ current_date }}.</ p >
7  {% endblock %} 

hours_ahead.html

1  {% extends "base.html>" %}
 2
3  {% block title %}Future time{% endblock %}
 4
5  {% block content %}
 6  < p > In {{ hour_offset }} hour(s), it will be {{ next_time }}.</ p >
7  {% endblock %} 

最后,在’./FOUR/’下,修改urls.py并且创建views.py,内容如下:

urls.py

1  from  django.conf.urls import  url
 2  from  FOUR.views import  *
3
4  urlpatterns = [
 5      url(r' ^time/$ ' , current_datetime),
 6      url(r' ^time/(\d{1,2})/$ ' , hours_ahead)
 7  ] 

views.py

 1  from  django.http import  Http404
  2  from  django.shortcuts import  render_to_response
  3  import  datetime
  4
 5  def  current_datetime(request):
  6      now = datetime.datetime.now()
  7  return  render_to_response(' current_datetime.html ' , { ' current_date ' : now })
  8
 9  def  hours_ahead(request, offset):
 10  try :
 11          offset = int(offset)
 12  except  ValueError:
 13  raise  Http404()
 14      dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
 15  return  render_to_response(' hours_ahead.html ' , { ' hour_offset ' : offset, ' next_time ' : dt }) 

至此,网站建立完成。运行后,便可进入以下网页:

网址 内容
http://127.0.0.1/time/ 显示当前时间(UTC时间)
http://127.0.0.1/time/num/ 显示当前时间+num小时(num=8则得到中国标准时间)

7、模板继承讲解

其实,模板继承部分,大家仔细看上面代码,就应该能够看个八九不离十。就是A模板继承了B模板,把B模板中供替换的块选几个替换了,形成一个新的模板。

下面,讲解其各种小细节,请结合代码学习。

标签代码 解释 备注

{% block name %}

#…

{% endblock %}

供继承的部分

注意,name不能重名

如果继承后重写了,则显示重写内容;

如果没有重写,则显示此处本来内容。

{% extends name %}

继承name模板

name可以是常量,也可以是变量

此标签必须是模板的第一个标记!否则,视为没进行模板继承。

此标签的路径也是上文中模板的路径,即TEMPLATES中的DIRS

{{ block.super name }}

这是个变量,是父模板中的name标签的内容

往往用于并不打算完全重写,而是添加几句话的情况

在此说明一点,继承是可以多层嵌套的,即A模板继承B模板,B模板又继承了C模板

8、模板包含与继承的比较

模板包含,是在基础模板中写了大家都有的内容,然后后面都包含它;而模板继承,则是在基础模板中写了大家都有的部分,又标出了大家不同的部分,而后进行填充。

因此,我认为继承更强大和顺手,而包含则更适用于比较简单的网页结构,因为太简单就没必要搞继承了。

9、小吐槽

书中认为,模板继承可以理解为模板包含的逆向思维。因为包含是对相同的代码段进行定义,而继承则是对那些不同的代码段进行定义。

但个人认为,此处理解并不恰当,继承是比包含更加强大的,而非单纯的相反思路。

当然,我这样是断章取义,书中自有其思路。另外,我看的是中文译本,也说不定和英文原版的意思有偏差呢~

故而此处仅仅小小吐槽一下,目的只为理清读者思路。

————————————————————————————————————————————————

至此,“模板”一章笔记完成,下一章是与数据库打交道——“模型”。

原文:http://www.cnblogs.com/icedream61/p/4518958.html

0 Comments

There are no comments yet

Leave a comment

Your email address will not be published.