一、模板概述
- 模板组成
HTML代码
动态插入的代码(挖坑、填坑逻辑控制代码)
- 作用
快速生成HTML页面
- 优点
模板的设计实现了业务逻辑与现实内容的分离
视图可以使用任何模板
- 模板处理
加载
渲染
二、定义模板
- 变量
- 视图传递给模板的数据
- 变量遵守标识符规则
- 语法
{{ var }}
- 注意
变量若不存在,则插入空的字符串
- 点语法
字典查询、属性或方法、数字索引
- 标签
- 语法
{% tag %}
- 作用
在输出中创建文本
控制逻辑和循环
- 单行注释
{# 注释内容 #}
- 标签if
- 语法1
{% if 条件 %}
语句
{% endif %}
- 语法2
{% if 条件 %}
语句1
{% else %}
语句2
{% endif %}
- 语法3
{% if 条件 %}
语句1
{% elif 条件 %}
语句2
{% elif 条件 %}
语句3
....
{% else %}
语句
{% endif %}
- 例如
{% if username %}
欢迎 <span> {{username}} </span> 回来
{%else%}
游客登录,新用户注册有惊喜!
{%endif%}
- 标签for
- 格式1
{% for 变量 in 列表 %}
语句
{% endfor %}
- 格式2
{% for 变量 in 列表 %}
语句1
{% empty %}
语句2
{% endfor %}
备注: 列表为空或列表不存在时执行语句2
- 格式3
{{forloop.counter}} 表示当前是第几次循环
- 例如
{% for student in students %}
<li>
我叫{{student.sname}},性别{{student.ssex}},今年{{student.sage}}岁,座右铭:"{{student.sbrief}}"。
</li>
{% endfor %}
- 例如
<ul>
{% for name in names %}
{% if forloop.counter|divisibleby:2 %}
<li style="color: red"> {{forloop.counter}} - {{name}} </li>
{% else %}
<li style="color: blue"> {{forloop.counter}} - {{name}} </li>
{% endif %}
{% empty %}
<li> 没有学生喔. </li>
{% endfor %}
</ul>
- 标签comment
- 作用
多行注释
- 例如
{% comment %}
<p>姓名: {{student.sname}}</p>
<p>性别: {{student.ssex}}</p>
<p>年龄: {{student.sage}}</p>
<p>座右铭: {{student.sbrief}} </p>
{% endcomment %}
- 标签ifequal、ifnotequal
- 作用
判断值1与值2是成立
- 例如
{% ifequal 'hello' 'world' %}
<h1> 喔,hello和world一样呢。 </h2>
{% endifequal %}
- 例如
# return render(request, 'meituan/testtemp.html',{'class1':'python03','class2':'python03'})
{% ifequal class1 class2 %}
<p>你们在同一个班级呢。</p>
{% else %}
<p>你们是不同班级。</p>
{% endifequal %}
- 过滤器
- 作用
在变量被显示前修改它
- 语法
{{ var|过滤器 }}
- lower 小写
<h1> {{str|lower}} </h1>
- upper 大写
<h1> {{str|upper}} </h1>
- join 传递参数(参数用引号引住)
<h1> {{list|join:'#'}} </h1>
- default 默认值(如果一个变量没有被提供或值为false、空,可以使用默认值)
<h1> {{username|default:'游客'}} </h1>
- date 根据给定格式转换日期为字符串
<h1> {{dateValue|date:'y-m-d'}} </h1>
- 标签inlcude
- 作用
加载模板并以标签内的参数渲染
- 格式
{% include '模板目录' 参数1 参数2 %}
- 例如:
# 不传递参数
{% include "content.html" %}
# 传递参数scoreList
{% include "content.html" with scores=scoreList %}
由零合一,先完成小细节,最后使用include一起包含进去!
- 标签url
- 作用
反向解析
- 格式
{% url 'namespace:name' p1 p2 %}
备注: 具体看下面详细说明。
- 标签block、extends
- 作用
用于模板的继承
备注: 具体看下面详细说明。
- 标签autoescape
- 作用
用于HTML转义
备注: 具体看下面详细说明。
- 标签csrf_token
- 作用
用于跨域请求伪造保护
- 格式
{% csrf_token %}
备注: 具体看下面详细说明。
三、反向解析
- 反向解析语法
{% url 'namespace:name' p1 p2 %}
- 项目的urls.py文件中
url(r'^meituan/', include('app.urls', namespace='app') )
- 应用的urls.py文件中
# 路由(带参数)
url(r'^goods/(\d+)/$',views.goods, name='goods'),
# 视图函数(带参数,和url与之对应)
def goods(request, page):
return HttpResponse('商品列表: 第%s页' % page)
- 模板(带有参数的)
<a href="{% url 'app:goods' 3 %}"> 商品列表 </a>
<a href="{% url 'app:students' student.id %}">{{ student.s_name }}</a>
备注:
name属性,即是给对对应的url添加一个名字;
但是使用 name 也存在一定的问题 ,比如在同一个项目中的不同的app中 name 可能会重名;
Django在反解URL时,会在项目全局顺序搜索,当查找到第一个name指定URL时,立即返回;
当不小心定义相同的name时,可能会导致URL反解错误,为了避免这种事情发生,引入了命名空间;
namespace命名空间;
反向解析场景: 正常情况下页面中设置的商品页链接地址都是goods/,而在服务器中路由如果发生一些细微的改变时,那么在页面中所有关于商品页链接地址都需要修改。为此可以使用反向解析解决此问题。
四、模板继承
- 作用
模板继承可以减少页面内容的重复定义,实现页面的复用。
- block标签
- 作用
在父木模板中预留区域,子模板去填充
- 语法
{% block 标签名 %}
{% endblock 标签名 %}
- extends标签
- 作用
继承模板,需要写在模板文件的第一行
- 语法
{% extends '父模板路径' %}
- 示例(定义父模板)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
{% block title %}
{% endblock title %}
<style>
#header,#footer{
height: 300px;
background: red;
line-height: 300px;
color: white;
text-align: center;
font-size: 30px;
}
#footer{
background: blue;
}
</style>
{% block style %}
{% endblock style %}
</head>
<body>
<!--父模板(很多页面中,头部和尾部都是一致的)-->
<div id="header">header</div>
<!--主体内容不同,需要各部分进行填充-->
<div id="content">
<!--主体内容不确定,就直接这样写即可-->
{% block content %}
{% endblock content %}
<br>
<br>
{% block test %}
{% endblock test %}
</div>
<div id="footer">footer</div>
</body>
</html>
- 示例(定义子模板1)
{% extends 'meituan/base.html' %}
{% block content %}
<h1> 首页 --- 主体内容 </h1>
<a href="{% url 'meituan:about' %}">关于我们</a>
{% endblock content %}
- 示例(定义子模板2)
{% extends 'meituan/base.html' %}
{% block title %}
<title>关于我们</title>
{% endblock title %}
{% block style %}
<style>
h2{
background: black;
color: white;
}
</style>
{% endblock style %}
{% block content %}
<h1> about关于我们 </h1>
{% endblock content %}
<!--继承,可以填充,也可以不填充-->
{% block test %}
<h2>我住隔壁,我姓王,你有事情我帮忙。</h2>
{% endblock test %}
五、HTML转义
- 问题
<!--views.py-->
return render(request,'meituan/test2.html',{'code': '<h1>今天是周一.</h1>'})
<!--HTML转义-->
{{code}}
<!--问题-->
将接受到的code当前普通字符串渲染
- 转义
将接受到字符串当成HTML代码渲染
<!--方式1-->
{{code|safe}}
<!--方式2-->
<!--关闭自动转义-->
{% autoescape off %}
{{code}}
{% endautoescape %}
六、CSRF
- 跨域请求伪造
某些恶意网站包含链接、表单、按钮、JS,利用用户登录在浏览器中认证,从而攻击服务
- 放置CSRF
在settings.py文件中MIDDLEWARE添加'django.middleware.csrf.CsrfViewMiddleware'
在form表单中添加 '{% csrf_token %}'
- 例如
<form action="{% url 'app:login' %}" method="post">
{% csrf_token %}
<input type="text" placeholder="输入验证码" name="text"> <br>
<input type="submit" value="验证">
</form>
在没有放置CSRF时,会报错一下错误Forbidden (403) CSRF verification failed. Request aborted. More information is available with DEBUG=True.。
七、验证码
- 作用
- 在用户注册、登录页面的使用使用,为了防止暴力请求,减轻服务器的压力
- 防止CSRF的一种方式
request.session.get["k1"],如果不存在则会报错,为了防止出错可以request.session.get('k1',none)
- 示例(视图之生成验证码)
# 验证码
def verifycode(request):
# 导入绘图模块
from PIL import Image,ImageDraw,ImageFont
# 导入随机函数模块
import random
# 文件操作
import io
# 定义变量,用于图片的背景色、宽、高
# random.randrange()返回指定递增基数集合中的一个随机数
bgcolor = (random.randrange(20,100),random.randrange(20,100),random.randrange(20,100))
width = 100
heigh = 50
# 创建图片
image = Image.new('RGB',(width,heigh),bgcolor)
# 创建画笔对象
draw = ImageDraw.Draw(image)
# 调用画笔的point函数,绘制噪点
for i in range(0,100):
xy = (random.randrange(0,width),random.randrange(0,heigh))
fill = (random.randrange(0,255),255,random.randrange(0,255))
draw.point(xy,fill=fill)
# 定义验证码的备选值
str = '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
# 随机选取4个值作为验证码
rand_str = ''
for i in range(0,4):
rand_str += str[random.randrange(0,len(str))]
# 构建字体对象
# font = ImageFont.truetype(path) # 指定路径加载字体
# font = ImageFont.load_default() # 加载一个默认的字体
# truetype(font=None, size=10, index=0, encoding="", layout_engine=None)
font = ImageFont.truetype('fonts/Songti.ttc',40)
# font = ImageFont.truetype('fonts/STXINGKA.ttf',40)
#构建字体颜色
fontcolor1 = (255, random.randrange(0, 255), random.randrange(0, 255))
fontcolor2 = (255, random.randrange(0, 255), random.randrange(0, 255))
fontcolor3 = (255, random.randrange(0, 255), random.randrange(0, 255))
fontcolor4 = (255, random.randrange(0, 255), random.randrange(0, 255))
# 绘制4个字
# def text(self, xy, text, fill=None, font=None, anchor=None, *args, **kwargs):
draw.text((5, 2), rand_str[0], font=font, fill=fontcolor1)
draw.text((25, 2), rand_str[1], font=font, fill=fontcolor2)
draw.text((50, 2), rand_str[2], font=font, fill=fontcolor3)
draw.text((75, 2), rand_str[3], font=font, fill=fontcolor4)
# 释放画笔
del draw
# 存入session,用于验证
request.session['verify'] = rand_str
# 文件操作
buff = io.BytesIO()
# 将图片保存在内存中,文件类型png
image.save(buff,'png')
# 将内存中图片数据返回给客户端,MIME类型为图片类型
# 直接是返回验证码图片
return HttpResponse(buff.getvalue(),'image/png')
PIL(Python Imaging Library)是Python一个强大方便的图像处理库,名气也比较大,不过只支持到Python 2.7。Pillow是PIL的一个派生分支,但如今已经发展成为比PIL本身更具活力的图像处理库。目前最新版本是3.0.0。
ython 3.x 安装Pillow: pip install Pillow
- 示例(视图之登录)
# 登录操作
def verifycodefile(request):
# 获取到session的flag标志值
flag = request.session.get("flag", True)
str = ''
if flag == False:
str = "请重新输入"
# 清空session
request.session.clear()
return render(request, 'meituan/verifycodefile.html', {"flag": str})
- 示例(视图之登录验证码验证)
# 登录验证
def verifycodecheck(request):
# 输入的验证码
# upper()转为大写
code1 = request.POST.get('verifycode').upper()
# session验证码
code2 = request.session['verify'].upper()
if code1 == code2: # 验证成功
return render(request,'meituan/success.html')
else: # 重新登录操作
request.session["flag"] = False
return redirect('/meituan/verifycodefile/')
- 示例(url管理器)
urlpatterns=[
url(r'^verifycode/$',views.verifycode), # 验证码测试
url(r'^verifycodecheck/$',views.verifycodecheck), # 验证操作
url(r'^verifycodefile/$',views.verifycodefile), # 带验证码登录
]
- 示例(模板)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录 | 验证码测试</title>
</head>
<body>
<!--验证码-->
<!--默认什么都添加,就是直接显示-->
<!--添加登录功能验证-->
<form action="/meituan/verifycodecheck/" method="post">
{%csrf_token%}
<input type="text" name="verifycode" />
<img src="/meituan/verifycode/" /> <br>
<input type="submit" value="验证" /> <br>
<span>{{flag}}</span>
</form>
</body>
</html>