本文为 Django 学习笔记,讲解模板的使用。
所有的代码见【Django】系列。
变量是视图传递给模板的数据,要遵守标识符规则。视图中的传递参数的键就是模板中的变量,语法 {{var}}
:
# views.py
def index(request):
# return HttpResponse("Mike is so handsome!")
return render(request, 'myApp/index.html', {"num": 10})
<body>
<h1>Mike is a good manh1>
<h1>num=*{{num}}*h1>
body>
如果使用的变量不存在,则插入的是空字符串。
在模板中使用点语法,首先将变量当作字典来查询,如果不是字典,则当作属性或方法,如果都不是则为数字索引。
在模板中调用对象:
<body>
<h1>Mike is a good manh1>
<h1>{{stu.sname}}h1>
<h1>num=*{{num}}*h1>
body>
在视图中进行传递:
def index(request):
student = Students.objects.get(pk=1)
return render(request, 'myApp/index.html', {"stu": student})
还可以在模板中调用对象的方法。首先在 models.py 中定义一个方法:
def getName(self):
return self.sname
但是,在模板中不能传递参数。
标签是用来在输出中创建文本的,并且可以控制逻辑和尊换。标签的语法为 {%tag%}
,无需缩进。
格式为:
{% if num %}
<h1>if - Mike is a good manh1>
{% endif %}
当 num 为 0 时,不显示。
<body>
<h1>学生列表h1>
<ul>
{% for stu in students %}
<li>
{{forloop.counter}}--{{stu.sname}}--{{stu.sgrade}}
li>
{% empty %}
<l1>目前没有学生l1>
{% endfor %}
ul>
body>
显示出 students 列表:
每行显示不同的颜色:
<body>
<h1>学生列表h1>
<ul>
{% for stu in students %}
{% if forloop.counter|divisibleby:2%}
<li style="color:red">
{{forloop.counter}}--{{stu.sname}}--{{stu.sgrade}}
li>
{% else %}
<li style="color:blue">
{{forloop.counter}}--{{stu.sname}}--{{stu.sgrade}}
li>
{% endif %}
{% empty %}
<l1>目前没有学生l1>
{% endfor %}
ul>
body>
效果为:
作用是注释多行
{% comment %}
注释的内容
{% endcomment %}
用来判断是否相等,格式为:
{% ifequal 值1 值2 %}
语句
{% endifequal %}
如果值 1 等于值 2 则执行语句
加载模板并以标签内的参数渲染,格式为:
{% include 模板目录 参数1 参数2%}
后面会详细介绍。
反向解析,格式为:
{% url'namespace: name' p1 p2 %}
后面会详细介绍。
用于跨站请求伪造保护,格式为:
{% csrf_token %}
后面会详细介绍。
用于模板的继承,后面会详细介绍。
用于 HTML 转义,后面会详细介绍。
在变量被显示前修改它,语法为 {{var|过滤器}}
。
lower、upper:将字符串过滤成小写/大写
过滤器可以传递参数,参数用引号引起来。join
的格式为 列表|join:'#'
:
{{list|join:'#'}}
如果一个变量没有被提供,或值为 False、空,可以使用默认值 default,格式为 {{var|default:'good'}}
:
{{test|default:'没有'}}
根据给定格式转换日期为字符串,使用 date,格式为 {{ dateVal:'y-m-d'}}
HTML 转义:escape。后面详细介绍
加减乘除
单行注释:{# 注释内容 #}
多行注释:见 comment
在主页中添加 a 标签,点击进入链接:
<a href="/good/">链接a>
此时若将 project/url.py 中的主页匹配修改,则该标签无法定位到 good.html 页面。要将 a 标签中的路径修改为 /sunck/good/
才能正常匹配到 url。但当 html 文件中有很多这样的路径时,修改十分不便,我们考虑使用反向解析技术(在 【Django 视图】有介绍)。a 标签中的路径通过两个 url 推理出来。
在 url 中配置如下:
# project/urls.py。使用namespace
re_path(r'^sunck/', include('myApp.urls', namespace="app")),
# myApp/urls.py
app_name = 'myApp' # Django2要加这句才能进行反向解析
# name最好与前面对应路径名
re_path(r'good/(\d+)/$', views.good, name="good"),
有了上面这几行代码,就可以动态生成 a 标签中的链接。上面写的 a 标签为硬链接,此时我们应该这样写:
{# 1为参数 #}
<a href="{% url 'app:good' 1 %}">链接a>
{# 多参数形式 #}
<a href="{% url 'app:good' 1 2 %}">链接a>
点击主页的链接后得到:
再将 project/urls.py 中的 sunck
删去,仍然可以正常匹配。
用一句话总结,反向解析实际上就是给 2 个 url 起了个别名。
可以减少页面内容的重复定义,实现页面的重用。很多网站的 header 和 footer 是相同的,我们可以写一个继承来实现所有的 header 和 footer。我们可以使用 block
和 extends
标签实现模板继承。语法如下:
{% block 标签名 %}
{% endblock 标签名 %}
{% extends '父模板路径' %}
我们定义一个父模板和子模版。首先在 templates/myApp 下添加文件 base,html:
<head>
<meta charset="UTF-8">
<title>Titletitle>
<style>
#header{
width:100%;
height:100px;
background-color:red;
}
#footer{
width:100%;
height:100px;
background-color:blue;
}
style>
head>
<body>
<div id="header">headerdiv>
<div id="main"> {# block继承 #}
{% block main %}
{% endblock main %}
<hr/>
{% block main2 %}
{% endblock main2 %}
div>
<div id="footer">footerdiv>
body>
html>
再在同路径下添加文件 main.html 和 detail.html:
{% main.html %}
{% extends 'myApp/base.html' %}
{% block main %}
<h1>Mike is a good manh1>
{% endblock main %}
{% block main2 %}
<h1>Cathy is a cute girlh1>
{% endblock main2 %}
{% detail.html %}
{% extends 'myApp/base.html' %}
{% block main %}
<h1>Mike is a nice manh1>
{% endblock main %}
效果如下:
如果在 index 视图中返回一个 html 标签,程序会自动将其当作字符串:
return render(request, 'myApp/index.html', {"code": "Mike is a good man
"})
如果我们要将接收的字符串当作 html 代码渲染,有 2 种方法:
{# safe过滤器 #}
{{code|safe}}
{# autoescape标签 #}
{# 关闭自动转义即可将字符串当作html代码渲染 #}
{% autoescape off %}
{{code}}
{% endautoescape %}
效果如下:
跨站请求伪造。某些恶意网站包含链接、表单、按钮、js 会利用登录的用户在浏览器中认证,从而攻击服务。
我们提交表单进行用户登录。模板页面:
{# postfile.html #}
<body>
<form action="/showinfo/" method="post">
姓名:<input type="text" name="username"/>
<hr/>
密码:<input type="password" name="passwd"/>
<hr/>
<input type="submit" value="登录"/>
form>
body>
{# showinfo.html #}
<body>
<h1>name:{{username}}h1>
<h1>pass:{{passwd}}h1>
body>
对应视图:
def postfile(request):
return render(request, 'myApp/postfile.html')
def showinfo(request):
name = request.POST.get('username')
pwd = request.POST.get('passwd')
return render(request, 'myApp/showinfo.html',{"username": name, "passwd": pwd})
直接登录用户会报错 Forbidden:
程序进行 CSRF 验证的原因为:
当我们在网页中查看源代码:
将其中的代码拷贝到本地,存为 html 文件,并将 action
路径改为服务器的路径 http://127.0.0.1:8000/showinfo/
:
再将本地的 html 文件用浏览器打开,就可以进行表单提交:
在 settings.py 中会默认开启 CSRF 验证:
MIDDLEWARE = [
# 'django.middleware.csrf.CsrfViewMiddleware',
]
注释掉这行代码,服务器和本地文件就都能登录了:
但是本地文件登录有很大的危险性,黑客可以写脚本无限循环登录,无数次在数据库中验证,攻击服务。
为了防止 CSRF,我们将上面的注释解开就好。但连自己的服务器都无法登录,可以在提交表单中加上:
{% csrf_token %}
这样以后,服务器就可以成功提交表单登录,而本地文件无法登录。但是,如果本地文件获取了 csrf 的 token 值,也可以伪装登录。查看服务器源代码可以得到 token 值:
将其添加到本地文件后,还是能够成功登录。在登录界面找到 token 的值为:
在用户注册、登录页面时使用,为了防止暴力请求,减轻服务器的压力,也是防止 csrf 的一种方式。
这里我们用代码生成一个验证码的图片:
def verifycode(request):
# 引入绘图模块
from PIL import Image, ImageDraw, ImageFont
# 引入随机函数模块
import random
# 定义变量,用于画面的背景色、宽、高
bgcolor = (random.randrange(20, 100), random.randrange(20, 100),
random.randrange(20, 100))
width = 100
height = 50
# 创建画面对象
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)
# 定义验证码的备选值
str = '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
# 随机选取4个值作为验证码
rand_str = ''
for i in range(0,4):
rand_str += str[random.randrange(0, len(str))]
# 构造字体对象。这里找自己路径中的字体库即可,注意是ttf格式
font = ImageFont.truetype(r'C:\Windows\Fonts\Arial.ttf', 40)
# 构造字体颜色
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['verify'] = rand_str
# 内存文件操作
import io
buf = io.BytesIO()
# 将图片保存在内存中,文件类型为.png
im.save(buf, 'png')
# 将内存中的图片数据返回给客户端,MIME类型为图片png
return HttpResponse(buf.getvalue(), 'image/png')
注意要提前用 pip install pillow
安装 pillow。调用该视图得到:
然后我们写一个输入验证码登录的模板 verifycodefile.html:
<body>
<form method="post" action="/verifycodecheck">
{% csrf_token %}
<input type="text" name="verifycode"/>
<img src="/verifycode/"/>
<input type="submit" value="登录">
form>
body>
配置 url 后得到结果:
点击登录按钮,判断验证码是否正确,并显示相应提示。verifycodecheck
的视图如下:
from django.shortcuts import render, redirect
def verifycodecheck(request):
code1 = request.POST.get("verifycode").upper() # 不区分大小写
code2 = request.session["verify"].upper()
if code1 == code2: # session中缓存的验证码与输入的验证码对比
return render(request, 'myApp/success.html')
else:
return redirect('/verifycodefile/')
输入正确的验证码后:
输入错误时要进行提示。在模板中加 span
标签:
<span>{{flag}}span>
在视图函数中对验证码进行验证:
from django.shortcuts import render, redirect
def verifycodefile(request):
f = request.session.get("flag", True) # 若首次取得flag,则为True
str = ""
if f == False:
str = "请重新输入"
request.session.clear() # 清空session
return render(request, "myApp/verifycodefile.html", {"flag": str})
def verifycodecheck(request):
code1 = request.POST.get("verifycode").upper()
code2 = request.session["verify"].upper()
if code1 == code2:
return render(request, 'myApp/success.html')
else:
request.session["flag"] = False # 将flag域置False
return redirect('/verifycodefile/')
输入错误的验证码后:
至此,关于 Django 模板的知识就介绍完啦。