Django后端笔记(四)模板

模板

模板的功能

在这里插入图片描述

产生html,控制页面上展示的内容。模板文件不仅仅是一个html文件。
模板文件包含两部分内容:

  1. 静态内容:css,js,html。
  2. 动态内容:用于动态去产生一些网页内容。通过模板语言来产生。

模板文件的使用

通常是在视图函数中使用模板产生html内容返回给客户端。

  1. 加载模板文件 loader.get_template
    获取模板文件的内容,产生一个模板对象。
  2. 定义模板上下文 RequeseContext
    给模板文件传递数据。
  3. 模板渲染产生html页面内容 render
    用传递的数据替换相应的变量,产生一个替换后的标准的html内容。
    view.py
from django.shortcuts import render,redirect
from django.template import loader,RequestContext

def my_render(request, template_path, context={
    
    }):
    # 1.加载模板文件,获取一个模板对象
    temp = loader.get_template(template_path)
    # 2.定义模板上下文,给模板文件传数据
    context = RequestContext(request, context)
    # 3.模板渲染,产生一个替换后的html内容
    res_html = temp.render(context)
    # 4.返回应答
    return HttpResponse(res_html)
    # /index
    x`
def index(request):
    # return my_render(request, 'booktest/index.html')
    return render(request, 'booktest/index.html')


模板文件加载顺序

在这里插入图片描述

  1. 首先去配置的模板目录下面去找模板文件。
  2. 去INSTALLED_APPS下面的每个应用的templates去找模板文件,前提是应用中必须有templates文件夹。

模板语言

模板语言简称为DTL。(Django Template Language)

模板变量

模板变量名是由数字,字母,下划线和点组成的,不能以下划线开头。
使用模板变量:{ {模板变量名}}
模板变量的解析顺序:

例如:{ { book.btitle }}

  1. 首先把book当成一个字典,把btitle当成键名,进行取值book[‘btitle’]
  2. 把book当成一个对象,把btitle当成属性,进行取值book.btitle
  3. 把book当成一个对象,把btitle当成对象的方法,进行取值book.btitle
    例如:{ {book.0}}
  4. 首先把book当成一个字典,把0当成键名,进行取值book[0]
  5. 把book当成一个列表,把0当成下标,进行取值book[0]
    如果解析失败,则产生内容时用空字符串填充模板变量。
    使用模板变量时,.前面的可能是一个字典,可能是一个对象,还可能是一个列表。
# /temp_var
def temp_var(request):
    '''模板变量'''
    my_dict = {
    
    'title':'字典键值'}
    my_list = [1,2,3]
    book = BookInfo.objects.get(id=1)
    # 定义模板上下文
    context = {
    
    'my_dict':my_dict, 'my_list':my_list, 'book':book}
    return render(request, 'booktest/temp_var.html', context)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板变量</title>
</head>
<body>
使用字典属性:{
   
   { my_dict.title }}<br/>
使用列表元素:{
   
   { my_list.1 }}<br/>
使用对象属性:{
   
   { book.btitle }}
</body>
</html>

在这里插入图片描述

模板标签

{% 代码段 %}
for循环:
{% for x in 列表 %}
# 列表不为空时执行
{% empty %}
# 列表为空时执行
{% endfor %}
可以通过=={ { forloop.counter }}==得到for循环遍历到了第几次。
{% if 条件 %}
{% elif 条件 %}
{% else %}
{% endif %}
关系比较操作符:> < >= <= == !=

注意:进行比较操作时,比较操作符两边必须有空格。
逻辑运算:not and or
model.py

# /temp_tags
def temp_tags(request):
    '''模板标签'''
    # 1. 查找所有图书的信息
    books = BookInfo.objects.all()
    return render(request,'booktest/temp_tags.html', {
    
    'books':books})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板标签</title>
    <style>
        .red {
      
      
            background-color: red;
        }
        .yellow {
      
      
            background-color: yellow;
        }
        .green {
      
      
            background-color: green;
        }
    </style>
</head>
<body>
<ul>
    {% for book in books %}
        {% if book.id <= 2 %}
            <li class="red">{
   
   { forloop.counter }}--{
   
   { book.btitle }}</li>
        {% elif book.id <= 5 %}
            <li class="yellow">{
   
   { forloop.counter }}--{
   
   { book.btitle }}</li>
        {% else %}
            <li class="green">{
   
   { forloop.counter }}--{
   
   { book.btitle }}</li>
        {% endif %}
    {% endfor %}
</ul>
</body>
</html>

过滤器

过滤器用于对模板变量进行操作。
date:改变日期的显示格式。
length:求长度。字符串,列表.
default:设置模板变量的默认值。
格式:模板变量|过滤器:参数

参考资料:(模板标签和内置过滤器)
http://python.usyiyi.cn/documents/django_182/ref/templates/builtins.html

<!DOCTYPE html>
<html lang="en">
{% load filters %}
<head>
    <meta charset="UTF-8">
    <title>模板过滤器</title>
    <style>
        .red {
      
      
            background-color: red;
        }
        .yellow {
      
      
            background-color: yellow;
        }
        .green {
      
      
            background-color: green;
        }
    </style>
</head>
<body>
<ul>
{% comment %}
    {% for book in books %}
        {# {% if book.id <= 2 %} #}
        {# {% if book.id|mod %}#}
        {% if book.id|mod_val:3 %}
            <li class="red">{
   
   { book.id }}--{
   
   { book.btitle|length }}--{
   
   { book.bpub_date|date:'Y年-m月-d日' }}</li>
        {% else %}
            <li class="green">{
   
   { book.btitle }}--{
   
   { book.bpub_date }}</li>
        {% endif %}
    {% endfor %}
{% endcomment %}
    <!-- <h1>html注释</h1> -->
</ul>
default过滤器:<br/>
{
   
   { content|default:'hello' }}
</body>
</html>

在这里插入图片描述

自定义过滤器

自定义的过滤器函数,至少有一个参数,最多两个

应用中新建templatetags目录,新建filter.py

# 自定义过滤器
# 过滤器其实就是python函数
from django.template import Library

# 创建一个Library类的对象
register = Library()


# 自定义的过滤器函数,至少有一个参数,最多两个
@register.filter
def mod(num):
    '''判断num是否为偶数'''
    return num%2 == 0


@register.filter
def mod_val(num, val):
    '''判断num是否能被val整除'''
    return num%val == 0

在模板文件中调用
在这里插入图片描述

模板注释

单行注 释:{# 注释内容 #}
多行注释:
{% comment %}
注释内容
{% endcomment %}
网页中查看源代码不能看到模板注释

模板继承

模板继承也是为了重用html页面内容。
在这里插入图片描述

在父模板里可以定义块,使用标签:
{% block 块名 %}
块中间可以写内容,也可以不写
{% endblock 块名%}
子模板去继承父模板之后,可以重写父模板中的某一块的内容。
继承格式:{% extends 父模板文件路径%}
{% block 块名 %}
{ { block.super}} #获取父模板中块的默认内容
重写的内容
{% endblock 块名%}

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}父模板文件{% endblock title %}</title>
</head>
<body>
<h1>导航条</h1>
{% block b1 %}
    <h1>这是父模板b1块中的内容</h1>
{% endblock b1 %}
{% block b2 %}
    <h1>这是父模板b2块中的内容</h1>
{% endblock b2 %}
<h1>版权信息</h1>
</body>
</html>

child.html

{% extends 'booktest/base.html' %}
{% block title %}子模板文件{% endblock title %}
{% block b1 %}
    {
   
   { block.super }}
    <h1>这是子模板b1块中的内容</h1>
{% endblock b1 %}

{% block b2 %}
    {
   
   { block.super }}
    <h1>这是子模板b2块中的内容</h1>
{% endblock b2 %}

html转义

编辑商品详情信息,数据表中保存的是html内容。
模板上下文中的html标记默认是会被转义的。

# /html_escape
def html_escape(request):
    '''html转义'''
    return render(request, 'booktest/html_escape.html', {
    
    'content':'<h1>hello</h1>'})
小于号< 转换为&lt;
大于号> 转换为&gt;
单引号' 转换为&#39;
双引号" 转换为 &quot;
与符号& 转换为 &amp;

要关闭模板上下文字符串的转义:可以使用 { { 模板变量|safe}}
也可以使用:
{% autoescape off %}
模板语言代码
{% endautoescape %}
模板硬编码中的字符串默认不会经过转义,如果需要转义,那需要手动进行转义。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>html转义</title>
</head>
<body>
html转义:<br/>
{
   
   { content }}<br/>
使用safe过滤器关闭转义:<br/>
{
   
   { content|safe }}<br/>
使用autoescape关闭转义:<br/>
{% autoescape off %}
    {
   
   { content }}
    {
   
   { content }}
{% endautoescape %}<br/>
模板硬编码中的字符串默认不会经过转义:<br/>
{
   
   { test|default:'<h1>hello</h1>' }}<br/>
手动进行转义:<br/>
{
   
   { test|default:'&lt;h1&gt;hello&lt;/h1&gt;' }}
</body>
</html>

在这里插入图片描述

csrf攻击

首先做一个登录页,让用户输入用户名和密码进行登录,登录成功之后跳转的修改密码页面。在修改密码页面输入新密码,点击确认按钮完成密码修改。
登录页需要一个模板文件login.html.修改密码页面也需要一个模板文件change_pwd.html.
login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<form method="post" action="/login_check">
    {% csrf_token %}
    用户名:<input type="text" name="username" value="{
     
     { username }}"><br/>
    密码:<input type="password" name="password"><br/>
    <input type="checkbox" name="remember">记住用户名<br/>
    <img src="/verify_code"><input type="text" name="vcode"><br/>
    <input type="submit" value="登录">
</form>
</body>
</html>

change_pwd.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>修改密码页面</title>
</head>
<body>
<form method="post" action="/change_pwd_action">
    {
    
    % csrf_token %}
    新密码:<input type="password" name="pwd">
    <input type="submit" value="确认修改">
</form>
</body>
</html>

显示登录页的视图login,验证登录的视图login_check,显示发帖页的视图change_pwd,处理修改密码的视图change_pwd_action.
加功能:
a)只有用户登录之后才可以进行修改密码操作。

# /change_pwd
@login_required
def change_pwd(request):
    '''显示修改密码页面'''
    # # 进行用户是否登录的判断
    # if not request.session.has_key('islogin'):
    #     # 用户未登录,跳转到登录
    #     return redirect('/login')

    return render(request, 'booktest/change_pwd.html')


# /change_pwd_action
@login_required
def change_pwd_action(request):
    '''模拟修改密码处理'''
    # # 进行用户是否登录的判断
    # if not request.session.has_key('islogin'):
    #     # 用户未登录,跳转到登录
    #     return redirect('/login')

    # 1.获取新密码
    pwd = request.POST.get('pwd')
    # 获取用户名
    username = request.session.get('username')
    # 2.实际开发的时候: 修改对应数据库中的内容...
    # 3.返回一个应答
    return HttpResponse('%s修改密码为:%s'%(username,pwd))

登录装饰器函数。

def login_required(view_func):
    '''登录判断装饰器'''
    def wrapper(request, *view_args, **view_kwargs):
        # 判断用户是否登录
        if request.session.has_key('islogin'):
            # 用户已登录,调用对应的视图
            return view_func(request, *view_args, **view_kwargs)
        else:
            # 用户未登录,跳转到登录页
            return redirect('/login')
    return wrapper
案例流程图:

在这里插入图片描述
1.登录正常网站后,浏览器保存的sessionid,没有退出
2.同时访问另一个网站,并点击上边的按钮
而第三方网站会利用sessionid修改最开始登录网站的信息
在这里插入图片描述

django防止csrf的方式:

  1. 默认打开csrf中间件。
    在这里插入图片描述

2) 表单post提交数据时加上{% csrf_token %}标签。
在这里插入图片描述

防御原理:

  1. 渲染模板文件时在页面生成一个名字叫做csrfmiddlewaretoken的隐藏域。
    在这里插入图片描述

  2. 服务器交给浏览器保存一个名字为csrftoken的cookie信息。
    在这里插入图片描述

  3. 提交表单时,两个值都会发给服务器,服务器进行比对,如果一样,则csrf验证通过,否则失败。

验证码

在用户注册、登录页面,为了防止暴力请求,可以加入验证码功能,如果验证码错误,则不需要继续处理,可以减轻业务服务器、数据库服务器的压力。

接下来的代码不要求手动写出来,因为这种代码在网上可以搜到很多。

1)安装包Pillow3.4.1。

pip install Pillow==3.4.1

点击查看PIL模块API,以下代码中用到了Image、ImageDraw、ImageFont对象及方法。

2)在booktest/views.py文件中,创建视图verify_code。

提示1:随机生成字符串后存入session中,用于后续判断。
提示2:视图返回mime-type为image/png。

from PIL import Image, ImageDraw, ImageFont
from django.utils.six import BytesIO

# /verify_code
def verify_code(request):
    # 引入随机函数模块
    import random
    # 定义变量,用于画面的背景色、宽、高 RGB
    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')

3)打开booktest/urls.py文件,配置url。

url(r'^verify_code/$', views.verify_code),

4)运行服务器,在浏览器中输入如下网址。

http://127.0.0.1:8000/verify_code/

5)浏览效果如下图:
在这里插入图片描述
可以多刷新几次看值会不会变。

def login_check(request):
    '''登录校验视图'''
    # 1.获取提交的用户名和密码
    username = request.POST.get('username')
    password = request.POST.get('password')
    remember = request.POST.get('remember') # None on

    # 获取用户输入验证码
    vcode1 = request.POST.get('vcode')
    # 获取session中保存的验证码
    vcode2 = request.session.get('verifycode')

    # 进行验证码校验
    if vcode1 != vcode2:
        # 验证码错误
        return redirect('/login')

反向解析

当某一个url配置的地址发生变化时,页面上使用反向解析生成地址的位置不需要发生变化。
根据url 正则表达式的配置动态的生成url。
在项目urls中包含具体应用的urls文件时指定namespace;

在这里插入图片描述

在应用的urls中配置是指定name;
在这里插入图片描述

在模板文件中使用时,格式如下:
{% url ‘namespace名字:name’ %} 例如{% url ‘booktest:fan2’%}
带位置参数:
{% url ‘namespace名字:name’ 参数 %} 例如{% url ‘booktest:fan2’ 1%}
带关键字参数:
{% url ‘namespace名字:name’ 关键字参数 %} 例如{% url ‘booktest:fan2’ id=1 %}

在重定向的时候使用反向解析:

from django.core.urlresolvers import reverse

无参数:

reverse('namespace名字:name名字')

如果有位置参数

reverse('namespace名字:name名字', args = 位置参数元组)

如果有关键字参数

reverse('namespace名字:name名字', kwargs=字典)

urls_reverse.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>url反向解析</title>
</head>
<body>
index链接:<br/>
<a href="/index">首页</a><br/>
url反向解析生成index链接:<br/>
<a href="{% url 'booktest:index' %}">首页</a>
/show_args/1/2:<br/>
<a href="/show_args/1/2">/show_args/1/2</a><br/>
动态产生/show_args/1/2:<br/>
<a href="{% url 'booktest:show_args' 1 2 %}">/show_args/1/2</a><br/>
/show_kwargs/3/4:<br/>
<a href="/show_kwargs/3/4">/show_kwargs/3/4</a><br/>
动态产生/show_kwargs/3/4:<br/>
<a href="{% url 'booktest:show_kwargs' c=3 d=4 %}">/show_kwargs/3/4</a>
</body>
</html>

view.py

# /url_reverse
def url_reverse(request):
    '''url反向解析页面'''
    return render(request, 'booktest/url_reverse.html')


def show_args(request, a, b):
    return HttpResponse(a+':'+b)


def show_kwargs(request, c, d):
    return HttpResponse(c+":"+d)


from django.core.urlresolvers import reverse
# /test_redirect
def test_redirect(request):
    # 重定向到/index
    # return redirect('/index')
    # url = reverse('booktest:index')

    # 重定向到/show_args/1/2
    # url = reverse('booktest:show_args', args=(1,2))

    # 重定向到/show_kwargs/3/4
    url = reverse('booktest:show_kwargs', kwargs={
    
    'c':3, 'd':4})
    return redirect(url)

urls.py

from django.conf.urls import url
from booktest import views


    url(r'^url_reverse$', views.url_reverse), # url反向解析页面
    url(r'^show_args/(\d+)/(\d+)$', views.show_args, name='show_args'), # 捕获位置参数
    url(r'^show_kwargs/(?P<c>\d+)/(?P<d>\d+)$', views.show_kwargs, name='show_kwargs'),  # 捕获关键字参数

    url(r'^test_redirect$', views.test_redirect),

猜你喜欢

转载自blog.csdn.net/qq_27251475/article/details/120321645