04 Django模板
-
基本概念
- 作为Web框架,Django提供了模板,用于编写html代码,还可以嵌入模板代码更快更方便的完成页面开发,再通过在视图中渲染模板,将生成最终的html字符串返回给客户端浏览器
- 模版致力于表达外观,而不是程序逻辑。模板的设计实现了业务逻辑view与显示内容template的分离,一个视图可以使用任意一个模板,一个模板可以供多个视图使用
-
组成
- 静态部分,包含html、css、js
- 动态部分,就是模板语言
-
模板文件
-
模板文件的加载顺序
- 首先去配置的模板目录下面去找模板文件
- 去INSTALLED_APPS下面的每个应用的templates去找模板文件,前提是应用中必须有templates文件夹
-
模板语言
- 模板语言简称为 DTL(Django Template Language)
-
模板变量
- 模板变量名是由数字,字母,下划线和点组成的,不能以下划线开头
-
使用模板变量
{{模板变量名}} |
-
模板变量的解析
-
解析顺序
-
{{ book.btitle }}
- 首先把 book 当成一个字典,把 btitle 当成键名,进行取值 book['btitle']
- 把 book 当成一个对象,把 btitle 当成属性,进行取值 book.btitle
- 把 book 当成一个对象,把 btitle 当成对象的方法,进行取值 book.btitle
-
{{ book.0 }}
- 首先把book当成一个字典,把0当成键名,进行取值book[0]
- 把book当成一个列表,把0当成下标,进行取值book[0]
-
- 如果解析失败,则产生内容时用空字符串填充模板变量。
- 使用模板变量时,. 前面的可能是一个字典,可能是一个对象,还可能是一个列表
-
-
模板标签
-
使用方法
-
{% 代码段 %} |
-
for 循环
{% for x in 列表 %} # 列表不为空时执行 {% empty %} # 列表为空时执行 {% endfor %} |
- 可以通过 {{ forloop.counter }} 得到 for 循环遍历到了第几次
-
条件语句
{% if 条件 %} {% elif 条件 %} {% else %} {% endif %} |
-
关系比较运算符
- > < >= <= == !=
- 进行比较运算时,比较操作符两边必须有空格
-
逻辑运算
- not and or
模板变量|过滤器:参数 |
- 其中 : 后不能有空格
-
常用过滤器
-
date
- 改变日期的显示格式
-
length
- 求长度。字符串,列表.
-
default
- 设置模板变量的默认值
-
-
自定义过滤器
- 至少有一个参数,最多只能有两个参数
-
示例:一个参数
-
在应用中创建templatetags目录,当前示例为"booktest/templatetags"(文件名不可更改),创建__init__文件,内容为空
-
在"booktest/templatetags"目录下创建 filters.py (文件名可自定义)文件,代码如下:
-
#导入Library类 from django.template import Library
#创建一个Library类对象 register=Library()
#使用装饰器进行注册 @register.filter #定义求余函数mod,将value对2求余 def iseven(value): return value%2 == 0
|
-
在 templates/booktest/temp_filter.html 中,使用自定义过滤器
-
首先使用load标签引入模块
-
{%load filters%} |
-
在遍历时根据编号判断奇偶,调用格式如下
显示 id 为偶数的图书 <ul> {% for book in books %} {% if book.id|iseven %} <li class="red">{{ book.id }}--{{book.btitle}}-- {{ book.bpub_date|date:"Y 年 m 月 d 日" }}</li> {% endif %} {% endfor %} </ul> |
-
示例:两个参数,其中接收一个参数
-
在 filters.py 中添加如下函数
-
@register.filter def mod(num, dividend): """判断 num 是否能被 dividend 整除""" return num % dividend == 0 |
-
在使用load标签引入模块后,调用格式如下:
显示 id 能被 3 整除的图书 <ul> {% for book in books %} {% if book.id|mod:3 %} <li class="red">{{ book.id }}--{{book.btitle}}-- {{ book.bpub_date|date:"Y 年 m 月 d 日" }}</li> {% endif %} {% endfor %} </ul> |
-
模板注释
-
单行注释
-
{# 注释内容 #} |
-
多行注释
{% comment %} 注释内容 {% endcomment %} |
-
模板继承
-
示意图
-
在父模板中可以定义块
-
{% block 块名 %} 块中间可以写内容,也可以不写 {% endblock 块名%} |
-
子模板的继承格式
{% extends 父模板文件路径%} |
-
子模块对块中的内容进行操作
{% block 块名 %} {{ block.super}} #获取父模板中块的默认内容 重写的内容 {% endblock 块名%} |
-
html 转义
-
在模板上下文中的 html 标记默认是会被转义的
- 小于号< 转换为<
- 大于号> 转换为>
- 单引号' 转换为'
- 双引号" 转换为 "
- 与符号& 转换为 &
-
关闭上下文字符串的方法
-
{{ 模板变量|safe}} |
-
使用 autoesacpe 标签
{% autoescape off %} 模板语言代码 {% endautoescape %} |
-
模板硬编码中的字符串默认不会经过转义
模板硬编码不转义:{{data|default:'<b>hello</b>'}} <br /> 模板硬编码手动转义:{{data|default:"<b>123</b>"}} |
-
csrf 攻击
-
基本概念
- CSRF全拼为Cross Site Request Forgery,译为跨站请求伪造
- 指攻击者盗用了你的身份,以你的名义发送恶意请求
- CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......
- 造成的问题包括:个人隐私泄露以及财产安全
-
示意图
-
防止方法
- 首先是重要的信息传递都采用 POST 方式而不是 GET 方式
- POST 方式避免 csrf 攻击需要采取特殊的措施
-
django 中的防止方法
- 在 setting.py 中启用 csrf 中间件
-
在 form 表单中使用标签 csrf_token
-
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>发帖页</title> </head> <body> <form method="post" action="/post_action/"> {% csrf_token %} 标题:<input type="text" name="title"/><br/> 内容:<textarea name="content"></textarea> <input type="submit" value="发帖"/> </form> </body> </html> |
-
django 中的保护原理
-
加入标签后,可以查看网页的源代码,会发现多了一个隐藏域
-
查看 cookie 信息
- 当启用中间件并加入标签csrf_token后,会向客户端浏览器中写入一条 Cookie 信息,这条信息的值与隐藏域 input 元素的 value 属性是一致的,提交到服务器后会先由 csrf 中间件进行验证,如果对比失败则返回 403 页面,而不会进行后续的处理
-
-
验证码
- 在用户注册、登录页面,为了防止暴力请求,可以加入验证码功能,如果验证码错误,则不需要继续处理,可以减轻业务服务器、数据库服务器的压力
-
实现验证码
-
在当前环境中安装包 Pillow 3.4.1
-
pip install Pillow==3.4.1 |
- PIL模块API,示例中使用了 Image、ImageDraw、ImageFont对象及方法
-
在booktest/views.py文件中,创建视图verify_code
- 随机生成字符串后存入session中,用于后续判断
-
视图返回 mime-type 为image/png
from PIL import Image, ImageDraw, ImageFont from django.utils.six import BytesIO ... def verify_code(request): #引入随机函数模块 import random #定义变量,用于画面的背景色、宽、高 bgcolor = (random.randrange(20, 100), random.randrange( 20, 100), 255) width = 100 height = 25 #创建画面对象 im = Image.new('RGB', (width, height), bgcolor) #创建画笔对象 draw = ImageDraw.Draw(im) #调用画笔的point()函数绘制噪点 for i in range(0, 100): xy = (random.randrange(0, width), random.randrange(0, height)) fill = (random.randrange(0, 255), 255, random.randrange(0, 255)) draw.point(xy, fill=fill) #定义验证码的备选值 str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0' #随机选取4个值作为验证码 rand_str = '' for i in range(0, 4): rand_str += str1[random.randrange(0, len(str1))] #构造字体对象,ubuntu的字体路径为"/usr/share/fonts/truetype/freefont" font = ImageFont.truetype('FreeMono.ttf', 23) #构造字体颜色 fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255)) #绘制4个字 draw.text((5, 2), rand_str[0], font=font, fill=fontcolor) draw.text((25, 2), rand_str[1], font=font, fill=fontcolor) draw.text((50, 2), rand_str[2], font=font, fill=fontcolor) draw.text((75, 2), rand_str[3], font=font, fill=fontcolor) #释放画笔 del draw #存入session,用于做进一步验证 request.session['verifycode'] = rand_str #内存文件操作 buf = BytesIO() #将图片保存在内存中,文件类型为png im.save(buf, 'png') #将内存中的图片数据返回给客户端,MIME类型为图片png return HttpResponse(buf.getvalue(), 'image/png') |
-
打开 booktest/urls.py 文件,配置 url
url(r'^verify_code/$', views.verify_code), |
-
浏览器效果图
- 刷新后验证码会发生变化
-
调用验证码
-
在booktest/views.py文件中,创建视图verify_show
-
def verify_show(request): return render(request,'booktest/verify_show.html') |
-
打开booktest/urls.py文件,配置url
url(r'^verify_show/$', views.verify_show), |
-
在templates/booktest/目录下创建verify_show.html
<html> <head> <title>验证码</title> </head> <body> <form method="post" action="/verify_yz/"> {%csrf_token%} <input type="text" name="yzm"> <img id="yzm" src="/verify_code/"> <span id="change">看不清,换一个</span> <br> <input type="submit" value="提交"> </form> </body> </html> |
-
浏览效果
-
验证
-
在 booktest/views.py 文件中,创建视图 verify_yz
-
def verify_yz(request): yzm=request.POST.get('yzm') verifycode=request.session['verifycode'] response=HttpResponse('no') if yzm==verifycode: response=HttpResponse('ok') return response |
-
打开booktest/urls.py文件,配置url
url(r'^verify_yz/$', views.verify_yz), |
-
浏览器中的效果
-
-
url 反向解析
- 当某一个url配置的地址发生变化时,页面上使用反向解析生成地址的位置不需要发生变化
- 反向解析应用在两个地方:模板中的超链接,视图中的重定向
-
要实现反向解析功能,需要如下步骤:
-
在 test4/urls.py中 为 include 定义 namespace 属性
-
url(r'^',include('booktest.urls',namespace='booktest')), |
-
在booktest/urls.py中为url定义name属性,并修改为fan2
url(r'^fan2/$', views.fan2,name='fan2'), |
-
django 2.0 版本需要指定 app_name
app_name = 'cart' |
-
在模板中使用url标签做超链接,此处为 templates/booktest/fan1.html 文件
<html> <head> <title>反向解析</title> </head> <body> 普通链接:<a href="/fan2/">fan2</a> <br> 反向解析:<a href="{%url 'booktest:fan2'%}">fan2</a> </body> </html> |
-
浏览器中查看源代码
-
在 booktest/urls.py 中,将 fan2 修改为 fan_show
url(r'^fan_show/$', views.fan2,name='fan2'), |
-
回到浏览器中,刷新,查看源文件如下图,可看出两个链接地址不一样
-
应用于视图重定向中
from django.shortcuts import redirect from django.core.urlresolvers import reverse
return redirect(reverse('booktest:fan2')) |
-
相关总结
-
在模板中
-
无参数
-
-
{% url 'namespace名字:name' %} 例如{% url 'booktest:fan2'%} |
-
位置参数
{% url 'namespace名字:name' 参数 %} 例如{% url 'booktest:fan2' 1 2%} |
-
关键字参数
{% url 'namespace名字:name' 关键字参数 %} 例如{% url 'booktest:fan2' id=1 name='lijunjie'%} |
-
在视图重定向中
-
首先导入 reverse 模块
-
from django.core.urlresolvers import reverse |
-
无参数
reverse('namespace名字:name名字') |
-
位置参数
reverse('namespace名字:name名字', args = 位置参数元组) |
-
关键字参数
reverse('namespace名字:name名字', kwargs=字典) |