Django模板的功能:产生html,控制页面上展示的内容
1. 模板文件
1.1 模板文件使用
Django中的模板文件不仅仅是一个html文件,包括两部分内容:
- 静态内容:css, js, html
- 动态内容:动态产生一些网页内容,通过DTL模板语言实现
使用模板文件分为以下三步:
1. 加载模板文件(获取模板文件) ----> 返回一个模板对象
2. 定义模板上下文(替换模板文件内的变量即向模板传递数据)
3. 渲染模板文件 ----> 替换模板变量,生成标准的HTML页面
4. 返回HttpResponse对象
示例1:django1.8.2适用
from django.template import loader, RequestContext
from django.http import HttpResponse
# 视图函数(URL处理函数)
def index(request):
# 1. 加载模板文件
template = loader.get_template('book/index.html')
# 2. 定义模板上下文(给模板文件传递数据)
context = RequestContext(request, {})
# 3. 模板渲染:产生标准的HTML美容
res_html = template.render(context, request)
# 4. 返回页面给浏览器
return HttpResponse(res_html)
示例2:django2.2适用
from django.template import loader
from django.http import HttpResponse
# 视图函数(URL处理函数)
def index(request):
# 1. 加载模板文件
template = loader.get_template('book/index.html')
# 2. 定义模板上下文(给模板文件传递数据)
context = {}
# 3. 模板渲染:产生标准的HTML内容
res_html = template.render(context, request)
# 4. 返回页面给浏览器
return HttpResponse(res_html)
注意:
- 模板文件配置在项目配置文件TEMPLATES项内部
- 快捷函数render(request, ‘模板文件’, context)实现模板内容的快速使用
1.2 模板文件搜索顺序
- 首先去配置的模板目录下面去找模板文件(项目目录下templates目录)
- 然后去INSTALLED_APPS下面的每个应用的templates去找模板文件
2. 模板语言
Django使用称为DTL的模板语言,主要包括四种类型:
- 变量
- 标签
- 过滤器
- 注释
2.1 变量
模板变量的变量名必须由字母、数字、下划线(不能以下划线开头)和点组成,语法如下:
{{ 变量 }}
字典查询,属性查询和列表索引查找都是通过一个点符号来实现
book.btitle格式:
- 先将book当做一个字典,btitle当做键取值
- 将book当做一个对象,先查找属性btitle,再查找方法btitle()(先属性后方法)
- 将book当做一个列表,btitle(数字时)当做索引取值
解析失败,产生内容时使用空字符串填充模板变量
2.2 模板标签
模板标签需要写结束标签
代码段:{% 代码段 %}
for标签:
{% for item in 列表 %}
循环逻辑
{{ forloop.counter }}表示当前是第几次循环,从1开始
{% empty %}
列表为空或不存在时执行此逻辑
{% endfor %}
if标签:
{% if ... %}
逻辑1
{% elif ... %}
逻辑2
{% else %}
逻辑3
{%endif%}
比较运算符:< > <= >= == != (运算符左右两侧不能紧挨变量或常量,必须有空格)
逻辑运算符:and or not
单行注释:{# 注释内容 #}
多行注释:{% comment %} {% endcomment %}
模板继承:{% extends '父模板路径' %}
模板内预留块:{% block 块名 %} {% endblock 块名 %}
加载过滤器模块:{% load 模块名 %}
2.3 过滤器
过滤器用于对模板变量进行操作
过滤器语法:模板变量|过滤器:参数
使用管道符号 | 来应用过滤器,用于进行计算、转换操作,可以使用在变量、标签中。
如果过滤器需要参数,则使用冒号:传递参数
length :返回字符串包含字符的个数或列表、元组、字典的元素个数 {{ 变量|length }}
default: 默认值 如果变量不存在或者计算结果为False时则返回默认值 {{ 变量|default:"参数" }}
date:日期格式化 {{ 变量|date:"参数" }}
完整内容参考:http://doc.codingdict.com/django/ref/templates/builtins.html
自定义过滤器
自定义过滤器其实就是一个python函数,使用步骤如下:
注意:自定义过滤器函数至少有一个参数(前面的模板变量),最多有两个参数
1. 在应用目录下创建一个名为templatetags的python包,内部创建filters.py用于存放自定义过滤器函数
2. filters.py内部引入Library模块,创建Library对象,编写过滤器函数并使用装饰器进行注册
from django.template import Library
# 创建Library的对象
register = Library()
# 自定义过滤器并注册
@register.filter
def mod(num):
"""判断是否为偶数"""
return num%2 == 0
# 两个参数的过滤器函数
@register.filter
def mod2(value, num):
"""判断是否为偶数"""
return value%num
3. 模块文档使用:加载过滤器函数,在模板变量处使用过滤器
<!DOCTYPE html>
<html lang="en">
{% load filters %} {#加载自定义过滤器所在的模块#}
<head>
<meta charset="UTF-8">
<title>自定义过滤器</title>
<style>
.red {
background-color: red;
}
.yellow {
background-color: yellow;
}
</style>
</head>
<body>
<p>图书信息:</p>
<ul>
{% for book in books %}
{% if book.id|mod %}
{# {% if book.id|mod2:3 %} 自定义过滤器函数传参 #}
<li class="yellow">{{ book.btitle }}</li>
{% else %}
<li class="red">{{ book.btitle }}</li>
{% endif %}
{% endfor %}
</ul>
</body>
</html>
2.4 模板继承
模板继承和类的继承含义是一样的,主要是为了提高代码重用,减轻开发人员的工作量
典型应用:网站的头部、尾部信息
继承格式:{% extends 父模板文件路径 %} (写在子模板文件的第一行)
- 父模板与子模板不一致的地方,应在父模板内预留块,语法如下:
{% block 块名 %}
块中间可以写内容,也可以不写
{% endblock 块名%}
- 子模板去继承父模板之后,可以重写父模板中的某一块的内容
{% block 块名 %}
{{ block.super}} #获取父模板中块的默认内容
重写的内容
{% endblock 块名%}
2.5 html转义
1. 模板对上下文传递的字符串进行输出时,默认会对以下字符自动转义:
- 小于号< 转换为<
- 大于号> 转换为>
- 单引号' 转换为'
- 双引号" 转换为 "
- 与符号& 转换为 &
2. 要关闭模板上下文字符串的转义:
- {{ 模板变量|safe }} (safe过滤器:让一个变量禁止转义)
- {% autoescape off %} (设置一段代码都禁用转义,接受on、off参数)
模板语言代码
{% endautoescape %}
3. 模板硬编码中的字符串默认不会经过转义,如果需要转义,那需要手动进行转义
模板硬编码不转义:{{ data|default:'<b>hello</b>' }}
3. CSRF攻击
CSRF:Cross Site Request Forgery,译为跨站请求伪造。
CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账…造成的问题包括:个人隐私泄露以及财产安全。
注意:网站开发时,一些页面需要登陆之后才能访问,我们需要进行登录判断(未登录跳转到登录页)
解决方法1:使用request.session.has_key()判断用户登录状态(多个页面都写不适用)
解决方法2:登录状态监测装饰器
def login_require(view_func):
def inner(request, *args, **kwargs):
if request.session.has_key('isLogin'):
return view_func(request, *args, **kwargs)
else:
return redirect('/books/login/')
return inner
CSRF伪造:跨站请求伪造
1. 登录正常网站后,浏览器保存了会话的sessionid,并且没有退出
2. 意外访问另一个网站,并且该网站返回了一个有可点击按钮或者图片等的页面,并且你进行了点击
- 关键点:伪造了向之前访问的网站发送请求(浏览器自己带着sessionid过去)
CSRF防护:
- Django默认打开csrf防护中间件
- 表单post提交添加csrf标记:使用csrf标签 ---> {% csrf_token %}
- 注意:csrf防护只针对post提交,因此重要数据建议使用post方式提交
防护原理:
- 渲染模板文件时在页面生成一个名字叫做csrfmiddlewaretoken的隐藏域
- 服务器交给浏览器保存一个名字为csrftoken的cookie信息
- 提交表单时,两个值都会发给服务器,服务器进行比对,如果一样,则csrf验证通过,否则失败
4. 验证码
在用户注册、登录页面,为了防止暴力请求,可以加入验证码功能,如果验证码错误,则不需要继续处理,可以减轻业务服务器、数据库服务器的压力。
# 生成验证码的视图函数:
from PIL import Image, ImageDraw, ImageFont
from django.utils.six import BytesIO
import random
def verify_code(request):
# 定义变量,用于画面的背景色、宽、高
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
# 内存文件操作(创建BytesIO对象)
buf = BytesIO()
# 将图片保存在内存中,文件类型为png
im.save(buf, 'png')
# 将内存中的图片数据返回给客户端,MIME类型为图片png
return HttpResponse(buf.getvalue(), 'image/png')
# 添加URL配置用于生成验证码(url(r"^/books/verify_code/", views.verify_code))
# 模板文件使用时添加img标签,src属性的URL地址请求/books/verify_code/即可生成验证码图片(同时往session_data里面写入生成的验证码内容)
# 登录验证时,先验证验证码,验证码不对直接刷新登录页面;正确再进行后续操作
5. URL反向解析
随着功能的增加会出现更多的视图,可能之前配置的url正则不够准确,于是要修改正则表达式,但是正则表达式一旦修改,之前所有对应的超链接都要修改,这很麻烦而且可能还会漏掉一些超链接忘记修改,有办法让链接根据正则表达式动态生成吗? 答:反向解析。
反向解析的作用:根据url正则表达式的配置动态的生成url
因此当某一个url配置的地址发生变化时,页面上使用反向解析的地方不需要变化(根据url正则表达式的配置动态的生成url)
反向解析使用:
1. 在项目URL配置文件包含具体应用的url配置文件时指定其url的命名空间(include函数的第二个参数)
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'books/', include('books.urls', namespace='books')),
]
2. 在应用的URL配置文件编写url配置时指定url配置的name属性(url函数的第三个参数)
urlpatterns = [
url(r'^index/$', views.index, name='index'), # 首页
url(r'^temp_var/$', views.temp_var, name='temp_var'), # 模板变量
url(r'^temp_tags/$', views.temp_tags, name='temp_tags'), # 模板标签
]
url反向解析用在两个地方:模板文件中的跳转链接、视图函数重定向的url链接地址
1. 模板文件中的跳转链接
使用格式:{% url 'namespace:urlname属性' %}
URL传参:
- 位置参数:{% url 'namespace:urlname属性' 参数1 参数2 %}
- 关键字参数:{% url 'namespace:urlname属性' id=1 age=18 %}
2. 视图函数重定向使用
使用格式:
from django.core.urlresolvers import reverse
redirect(reverse('namespace:name属性'))
URL传参:
- 位置参数:reverse('namespace:name属性', args=位置参数元组)
- 关键字参数:reverse('namespace:name属性', kwargs={参数})