Django笔记 —— 模板

时间:2022-11-05 07:06:36

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

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

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

------------------------------------------------------------------------------------------------------------------------------------------------

 0、代码示例

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

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

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

 <html>
<head>
</head>
<body>
Hello~I'm {{name}}^.^<br />
</body>
</html>

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

 name    qiqi

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

 from django.conf.urls import url
from four.views import home urlpatterns = [
url(r'^$', home),
]

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

 from django.http import HttpResponse
from django.template import Template, Context def home(request):
tin = open('./templates/home.html')
html = tin.read()
tin.close() cin = open('./templates/home.txt')
inf = {}
for line in cin.readlines():
a,b = line.split()
inf[a] = b
cin.close() t = Template(html)
c = Context(inf)
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处也改了两行):

 from django.http import HttpResponse
from django.template import Context
from django.template.loader import get_template def home(request):
cin = open('./templates/home.txt')
inf = {}
for line in cin.readlines():
a,b = line.split()
inf[a] = b
cin.close() t = get_template('home.html')
c = Context(inf)
return HttpResponse(t.render(c))

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

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

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

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

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

 from django.shortcuts import render_to_response

 def home(request):
cin = open('./templates/home.txt')
inf = {}
for line in cin.readlines():
a,b = line.split()
inf[a] = b
cin.close() return render_to_response('home.html', inf)

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

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

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

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

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

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

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

 from django.shortcuts import render_to_response

 def home(request):
name = 'qiqi'
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

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

current_datetime.html

 {% extends "base.html" %}

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

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

hours_ahead.html

 {% extends "base.html" %}

 {% block title %}Future time{% endblock %}

 {% block content %}
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
{% endblock %}

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

urls.py

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

views.py

 from django.http import Http404
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 }) def hours_ahead(request, offset):
try:
offset = int(offset)
except ValueError:
raise Http404()
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
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、小吐槽

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

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

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

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

------------------------------------------------------------------------------------------------------------------------------------------------

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