【Django】模板

本文为 Django 学习笔记,讲解模板的使用。

运行环境

  • Windows 10
  • Pycharm Community Edition 2020.1.3
  • Django 3.0.8

所有的代码见【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

格式为:



{% if num %}
    <h1>if - Mike is a good manh1>
{% endif %}

当 num 为 0 时,不显示。

for



<body>
    <h1>学生列表h1>
    <ul>
        {% for stu in students %}
            <li>
                {{forloop.counter}}--{{stu.sname}}--{{stu.sgrade}}
            li>
        {% empty %}
            <l1>目前没有学生l1>
        {% endfor %}
    ul>
body>

显示出 students 列表:

【Django】模板_第1张图片

每行显示不同的颜色:

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

效果为:

【Django】模板_第2张图片

comment

作用是注释多行

{% comment %}
注释的内容
{% endcomment %}

ifequal, ifnotequal

用来判断是否相等,格式为:

{% ifequal 值1 值2 %}
语句
{% endifequal %}

如果值 1 等于值 2 则执行语句

include

加载模板并以标签内的参数渲染,格式为:

{% include 模板目录 参数1 参数2%}

后面会详细介绍。

url

反向解析,格式为:

{% url'namespace: name' p1 p2 %}

后面会详细介绍。

csrf_token

用于跨站请求伪造保护,格式为:

{% csrf_token %}

后面会详细介绍。

block, extends

用于模板的继承,后面会详细介绍。

autoescape

用于 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>

点击主页的链接后得到:

【Django】模板_第3张图片

再将 project/urls.py 中的 sunck 删去,仍然可以正常匹配。

用一句话总结反向解析实际上就是给 2 个 url 起了个别名

模板继承

可以减少页面内容的重复定义,实现页面的重用。很多网站的 header 和 footer 是相同的,我们可以写一个继承来实现所有的 header 和 footer。我们可以使用 blockextends 标签实现模板继承。语法如下:

{% 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 %}

效果如下:

【Django】模板_第4张图片

HTML 转义

如果在 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 %}

效果如下:

【Django】模板_第5张图片

CSRF

跨站请求伪造。某些恶意网站包含链接、表单、按钮、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:

【Django】模板_第6张图片

程序进行 CSRF 验证的原因为:

当我们在网页中查看源代码:

【Django】模板_第7张图片
)

将其中的代码拷贝到本地,存为 html 文件,并将 action 路径改为服务器的路径 http://127.0.0.1:8000/showinfo/

【Django】模板_第8张图片

再将本地的 html 文件用浏览器打开,就可以进行表单提交:

【Django】模板_第9张图片

在 settings.py 中会默认开启 CSRF 验证:

MIDDLEWARE = [
    # 'django.middleware.csrf.CsrfViewMiddleware',
]

注释掉这行代码,服务器和本地文件就都能登录了:

【Django】模板_第10张图片

但是本地文件登录有很大的危险性,黑客可以写脚本无限循环登录,无数次在数据库中验证,攻击服务。

为了防止 CSRF,我们将上面的注释解开就好。但连自己的服务器都无法登录,可以在提交表单中加上:

{% csrf_token %}

这样以后,服务器就可以成功提交表单登录,而本地文件无法登录。但是,如果本地文件获取了 csrf 的 token 值,也可以伪装登录。查看服务器源代码可以得到 token 值:

【Django】模板_第11张图片

将其添加到本地文件后,还是能够成功登录。在登录界面找到 token 的值为:

a3V0OO.png

验证码

在用户注册、登录页面时使用,为了防止暴力请求,减轻服务器的压力,也是防止 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。调用该视图得到:

【Django】模板_第12张图片

然后我们写一个输入验证码登录的模板 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 后得到结果:

【Django】模板_第13张图片

点击登录按钮,判断验证码是否正确,并显示相应提示。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/')

输入正确的验证码后:

【Django】模板_第14张图片

输入错误时要进行提示。在模板中加 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】模板_第15张图片

至此,关于 Django 模板的知识就介绍完啦。

你可能感兴趣的:(#,Python,python,html,web,django)