Django的项目---管理员模块(下)_09【可读性更好版本】

(0)摘要

# 课程链接:

全新 django3 入门到项目实战(零基础学django、项目开发实践、大学毕业设计均可用)_哔哩哔哩_bilibili

# 课程覆盖:

        管理员模块_(4-11 ~~~ 4-12 的课程),其中 ajax 初识分两节笔记


#  若无闲事挂心头,便是人间好时节

(1)4_11 图片验证码(校验)

# (1)验证码校验

                1)校验的逻辑。我们知道不同用户登录时的验证码是不一样的,但是我们又想要实现一对一的校验。那么就需要利用 session 队列了。在 session 中,我们存放了不同用户的凭证和 info ,也就是说在生成了验证码后,服务器给浏览器返回验证码信息,我们可以将相应的验证码写入 session 中。

                 

                要理解上面的逻辑,需要重新看一下我们写的中间件。假设浏览器想访问的 url 为 /admin/list/ ,那么一开始的时候是判断的是否访问里面列表的两个 url 不是的话,就直接看看 session 的 info 字段有没有写入东西,如果没有就重定向到 /login/ 。


# (2)图片验证码校验的实现

                1)我们在生成了验证码后,我们把验证码的真值写到 session 中,用图中 request.session["img_code"] = code_words 来进行设置,同时我们可以为这个验证码设置有效时间是 60 秒,具体可以看图中注释。

                2)重新修改 login 的视图函数的逻辑。由于现在用户还要输入验证码,因此在 form.cleaned_data 字典中得到的就多一个字段的数据了。(我们在钩子方法中加入了新的验证码字段 verify,后面会给出源码)。为了不妨碍后续的用户名和密码的判断,我们选择 pop 的方式从这个字典数据中取出数值,并且删除 verify 字段,那么这里得到的就是用户输入的验证码 user_input_code ;然后我们再重新取出 request.session.get("img_code", "") 就是从session 队列中取出验证码的真值,我们将输入的和真值统一大写后进行匹配,如果不一致就返回错误。

                 

                3)考虑到我们先前将 session 信息的有效时间设为了 60 秒,因此我们还要再设置一下 session 的有效时间,比如七天免登录。代码如下图所示。我们要理解整个一个验证的过程是一个用户的。逻辑要转过来。


# (3)登录的源码

# -*- coding=utf-8 -*- 
# github: night_walkiner
# csdn: 潘迪仔


from django import forms
from app01 import models
from django.shortcuts import render, redirect, HttpResponse

from app01.utils.encrypt import md5
from app01.utils.check_words import check_code
from app01.utils.bootstrap import BootStrapForm

class LoginForm(BootStrapForm):
    username = forms.CharField(
        label="用户名",
        widget=forms.TextInput,
        required=True
    )
    password = forms.CharField(
        label="密码",
        widget=forms.PasswordInput,
        required=True
    )
    verify = forms.CharField(
        label="验证码",
        widget=forms.TextInput,
        required=True
    )

    # 钩子方法---来判断获得密码的值
    def clean_password(self):
        pwd = self.cleaned_data.get("password")
        # 对密码进行 md5() 加密
        return md5(pwd)


def admin_login(request):
    """登录"""
    if request.method == "GET":
        form = LoginForm()
        context = {
            "form": form,
        }
        return render(request, 'login.html', context)

    form = LoginForm(data=request.POST)
    if form.is_valid():
        # cleaned_data 就是获得的 post 数据。比如结果是 {'username': '123', 'password': '123', 'verify': xxxx}

        user_input_code = form.cleaned_data.pop("verify")
        img_code = request.session.get("img_code", "")
        # .upper() 是可以将字符串里面全部都大写
        if img_code.upper() != user_input_code.upper():
            form.add_error("verify", "验证码输入错误")
            return render(request, 'login.html', {'form': form})


        admin_info = models.Admin.objects.filter(**form.cleaned_data).first()
        if not admin_info:
            form.add_error("password", "用户名或密码错误")
            return render(request, 'login.html', {'form': form})

        # 用户密码校验成功后,网站生成随机字符串;写到用户浏览器的 cookie 中;再写入到 session 中。
        # admin_info 是从数据库中取出的。
        request.session["info"] = {"id": admin_info.id, "name": admin_info.username}
        # 设置 7 天免登录
        request.session.set_expiry(60 * 60 * 24 * 7)
        return redirect('/admin/list/')

    return render(request, 'login.html', {"form": form})



# 注销
def admin_logout(request):
    """注销函数"""
    # 清楚当前用户的 session 信息
    request.session.clear()

    # 重定向回登录页面
    return redirect('/login/')


def check_words(request):
    # 验证码的生成
    # (1) 导入图片验证码插件。
    img, code_words = check_code()

    # 验证码的校验。每个用户在登录时候,肯定是对应不同的验证码
    # 我们利用 session 每个用户对应不同的信息,然后将各自的验证码放入到 session 的新增字段 img_code 中。
    request.session['img_code'] = code_words
    # request.session.set_expiry(x) 是用来设置验证码有效时间为 x 秒的
    request.session.set_expiry(60)


    # (2) 写入内存(Python3)
    # 其实这一步我们是要把 img 写入辅存(磁盘),然后再从辅存取出使用的。但是我们知道cpu、内存、辅存的三者的写入写出关系,
    # 因此为了加快这个调用逻辑,我们可以将图片对象暂时存在内存中。那么具体的操作如下:
    from io import BytesIO  # 这是操作内存的一个工具
    # 实例化内存流对象
    stream = BytesIO()
    # (3) 使用 img.save() 存在内存流中。这里可以理解为,保存了 stream.png 图片,只是存在内存而已。
    img.save(stream, 'png')
    # (4) stream.getvalue() 就是取出保存在内存中的图片验证码。其名称就是 stream 了



    return HttpResponse(stream.getvalue())



(2)4_12 初识 ajax_入门

# (1)ajax 请求

                1)在前面我们在解决浏览器向网站发送请求时,使用的都是基于 form 表单形式的提交请求,也就是 GET、POST 两种。这种方式虽然容易,但是每一次点击提交的时候,都需要刷新一次页面。

                2)除了上面的形式,我们可以基于 ajax 向后台发送请求,这种可以理解为偷偷的向服务器发送请求,也就是不刷新页面的请求方式。这里依赖 JQuery 库,这是一个经典的js框架了。然后就是编写 ajax 代码。 

                 

                下面是 ajax 请求代码的注释。

// 使用了 python 的注释方式。。。

$.ajax({
    url: "请求的地址",    # 这里是请求的地址 
    type: "get",         # 这是请求的类型是 get 还是 post
    data: {              # 如果是 get 请求的话,那么 data 就是传到后台的参数
        n1: 123,
        n2: 456
    },        
    # 如果请求成功了,后端就会返回一个 res 参数,然后我们可以直接获取到
    success:function(res){
        console.log(res)
    }
    
})

               


# (2)JavaScript 相关知识_基于 Dom 方式的绑定点击事件

                1)基于 Dom 方式的绑定点击事件。即形如下图所示的方法。(该页面是 task_list.html ,我们已经定义了相关的视图函数。)

                 那么执行上述代码后,我们可以通过点击前端页面的点击按钮,就可以在控制台(浏览器的)输出一次点击了按钮。

               

                2)如果我们想要点击的时候,发送一个请求,就需要修改 js 的代码。在此之前,我们提前创建一个 url 为 "/task/ajax/"。并且给出的响应值是 "接收成功了",然后我们开始按照上面 ajax 请求的格式和注释,来修改 js 的代码。如下图所示。【url 的地方有个坑,具体看后面我填坑】

                然后我们测试点击按钮,可以看到返回值 res :"接收成功啦"。

                 

                我们可以查看请求时候的 url,如下图所示 

                 

                当然,我们也可以在后台的视图函数,即 task_ajax (就是我们所请求的那个 url)

获得 get 请求传来的参数。如下图的代码所示。

                3)如果我们的 ajax 使用的是 post 请求,也就是在上面的代码中,将 type: "get" 改成 type: "post" ,再次回到前端页面按点击就会报错。

                如何免除 csrf_token 认证,这里有个知识点。就是导入如下的包,如何在函数前引用。这是因为我们之前

                我自己也补充一个知识点,post 请求后,我们也可能出现下面的问题,即状态码是500 的,这是因为我们在 ajax 代码上的 url: "/task/ajax" 是不可以的,需要改成 url: "/task/ajax/"。

                于是,在前面两个问题解决后,我们再次点击按钮,显示了接收成功。

                 

                这样,我们就可以使用 post 请求的参数了。


# (3)JavaScript 相关知识_基于 JQuery 方式的绑定点击事件

                1)基于 JQuery 方式的绑定点击事件。即形如下面的方式的。就是我们将原来的 onclick 属性给去掉,利用 id 属性来绑定事件。然后 js 代码块中,按照格式 $(function () { 函数体 }) 的格式来写 js 代码,那么这种写法的话,当页面加载完成后会自动执行函数体的内容。

                2)因此,我们利用自动加载内容,我们定义一个绑定事件的函数 bindBtn1Event(),定义的方式是 function 函数名() { 函数体 }, 那么这个函数的函数体内容就是页面加载完成后会去找到 id 绑定的按钮,也就是一旦发生了点击后,就会执行click()里面的 ajax 内容。


# (4)Json 格式

                1)如果我们使用 JQuery 的方式做 ajax 请求的话,一般情况下,后台不会以页面返回,而是以 json 格式返回数据。举个例子来说,比如我们 python 后端生成的一个字典准备返回前端,但是这里还需要进行 json 化数据。具体方法见下例。之所以 json 化是因为以 json 格式传回前端能够让前端对这些数据进一步的操作。

                当然,我们也可以利用 Django 内置的一种方法来写,即使用 JsonResponse 来返回,那么写法就是下图这样。这种方法是自动的转换,上面的是我们手动转换。

                2)我们将 json 格式内容返回到前端之后,那么实际上返回的是一个字符串,因此前端要对这个 json 格式数据反序列化成 js 里面的对象。这里我们就需要在 ajax 中新增一个对象。 

                 

                然后我们就可以取后台返回的数据的值了。那么代码如下:

                我们到前端去触发点击事件,就可以看到控制台输出了我们先前定义的 status 和 data 字段分布对应的值了。其实对于 ajax 而言,其内部返回和处理的数据格式都是 json 格式。


# (5)实例 2 ,将前端的 input 的内容返回到后端。(利用 ajax)

                1)问题就是我们要将前台的输入框里面的值,通过 ajax 请求发送到后台。那么我们在前端添加了相应的输入框后,然后设置新的点击事件。首先是新增的 html 代码。

                基于上面的 ajax 代码,我们有如下的代码。首先是,我们如果想在 ajax 代码中,获取输入框的内容,可以利用他们的 id 值来进行类似索引的效果。即 $("#id值").val() 来获取,那么 $("#id值") 是获取到对应的 id 的输入框,.val() 方法就是获取这个输入框的内容。

                然后我们打开前端页面,在输入框输入内容,当触发了点击按钮后,成功获取到了内容并且传到了后台。 


# (6)实例3,如果输入框有很多的情况

                1)如果输入 框有很多的情况下,我们可以用 form 标签将这些输入的标签包裹起来。那么这个时候里面的 input 标签就可以不用考虑 id 属性了,但是必须要有 name 属性。而 form 标签必须设置 id 属性。【最后的那个 input 的 id = "btn3",忘记改了】

                2)然后修改 ajax 的代码,如下图所示,红框中的代码,$("#form3").serialize() 就可以将 form 内的 input 标签的内容全部获得并且打包作为数据,以发送到后台。

                3)那么我们测试了之后,得到下图的结果。显然是可以的 


(3)4_12 初识 ajax_小案例实战

# (1)案例实战

                1)数据库表创建。下图是创建任务列表的数据库表的代码。对于 level_choices 以及外键的相关知识这个,可以去看员工表的那一节有讲解。我们设计好表结构,然后执行迁移命令即可。

                2)使用 ModelForm 来渲染前端。前端的代码如下,我们也添加了相应的样式控制的代码。

                页面效果显示。


# (2)案例实现的测试。

                1)前端的 html 代码。

                2)基于 JQuery 的代码。这里记得复习下上面的,逻辑就是页面渲染完后,自动执行代码,如果用户触发了绑定的事件,就执行相应的代码。

                3)视图函数的编写,这里是初步测试的。

                4)初步结果查看。

               

                前端接收的返回值,即 res.status


# (3)案例完善,添加数据校验和错误信息

                1)数据校验功能。

                2)错误信息的问题。我们知道 form.errors 里面包含了所有的错误信息,如果提交的数据是有问题的,那么我们可以使用 forms.errors.as_json() 就能以 json 的格式传回前端。

                那么前端传回的 json 数据如下:

                 实际上使用了 as_json() 方法传回的错误信息,不是很好阅读,因此我们取消掉这个,也就是上面代码 form.errors.as_json() 变为 form.errors,就能看到返回的错误信息更加方便阅读了。

                3)显示错误的前端 html 代码如下,

                4)js 代码如下,见红框的注释。$("id_" + name) 是索引到这个 id 值对应的标签,那么 $("id_" + name).next() 就是该标签的下一个标签,然后 $("id_" + name).next().text() 就是下一个标签的内容。之所以是 data[0] ,是因为传回来的错误信息是个列表。

                5)错误信息的视图函数


【补充知识】

# (1)models.py 数据表中的 models.TextField(versbose_name="")

               

                这个数据库定义的方式,就是存储文本的。如果我们使用 ModelForm() 来渲染的话,默认的样式 widgets = { "detail": forms.Textarea },然后前端的页面就是下图这样的。

                如果我们想要改变成普通的输入框类型,那么我们也可以通过设置 widgets 来实现。但是需要导入 forms 模块。那么类的定义就变为这样,见红框处,即 widgets = { "detail": forms.TextInput }

                结果如图所示:


# (2)models.py 下如果涉及到外键的话,如何取到外键的值,而不是取对象。

                就是如下图所示,负责人其实是对应的 Admin 表,因此我们要使用 __str__ 方法。

                要解决这个问题,就需要去 Admin 表中,写一个方法,即如下的代码。

                结果如下:


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值