美文网首页零基础使用Django2.0.1打造在线教育网站
零基础使用Django2.0.1打造在线教育网站(十二):错误信

零基础使用Django2.0.1打造在线教育网站(十二):错误信

作者: 啃饼小白 | 来源:发表于2018-08-03 07:54 被阅读6次

    写在前面

    本篇笔记主要解决登录页面时的另一种验证方法,错误信息提示,cookie和session介绍这3个问题。

    本篇笔记对应于第十二篇代码,对应于github的位置是https://github.com/licheetools/eduline

    基于类的用户登录实现

    在前面我们实现用户登录是基于视图函数来实现的,也就是下面这段代码:

    # 当我们配置的url被这个view处理时,将会自动传入request对象.
    def user_login(request):
        # 前端向后端发送的请求方式有两种: get和post
    
        # 登录提交表单时为post
        if request.method == "POST":
            # username,password为前端页面name的返回值,取到用户名和密码我们就开始进行登录验证;取不到时为空。
            user_name = request.POST.get('username', '')
            pass_word = request.POST.get('password', '')
            # 取值成功返回user对象,失败返回null
            user = authenticate(username=user_name, password=pass_word)
            if user is not None:
                # login 有两个参数:request和user。我们在请求的时候,request实际上是写进了一部分信息,然后在render的时候,这些信息也被返回前端页面从而完成用户登录。
                login(request, user)
                # 页面跳转至网站首页 user request也会被带回到首页,显示登录状态
                return render(request, 'index.html')
            else:
                # 说明里面的值是None,再次跳转回主页面并报错
                return render(request, "login.html", {'msg': '用户名或者密码错误!'})
        # 获取登录页面时为get
        elif request.method == "GET":
            # render的作用是渲染html并返回给用户
            # render三要素: request ,模板名称 ,一个字典用于传给前端并在页面显示
            return render(request, "login.html", {})
    
    

    同时在eduline/urls.py文件的路径配置为:

    from users.views import user_login
    
    path('login/', user_login, name="login")
    

    现在我们采用基于类的方法来实现用户的登录,因为基于类可以便于重载和调用。下面是基于类的代码,小伙伴们试着体验一下两者的不同之处吧:

    #  基于类实现用户的登录,它需要继承view
    class LoginView(View):
        # 不需要判断,直接调用get方法
        def get(self, request):
            # render的作用是渲染html并返回给用户
            # render三要素: request ,模板名称 ,一个字典用于传给前端并在页面显示
            return render(request, "login.html", {})
    
        # 不需要判断,直接调用post方法
        def post(self, request):
            # username,password为前端页面name的返回值,取到用户名和密码我们就开始进行登录验证;取不到时为空。
            user_name = request.POST.get('username', '')
            pass_word = request.POST.get('password', '')
            # 取值成功返回user对象,失败返回null
            user = authenticate(username=user_name, password=pass_word)
            if user is not None:
                # login 有两个参数:request和user。我们在请求的时候,request实际上是写进了一部分信息,然后在render的时候,这些信息也被返回前端页面从而完成用户登录
                    login(request, user)
                # 页面跳转至网站首页 user request也会被带回到首页,显示登录状态
                    return render(request, "login.html")
            else:
                # 说明里面的值是None,再次跳转回主页面并报错
                return render(request, "login.html", {'msg': '用户名或者密码错误!'})
    

    同时在eduline/urls.py文件的路径配置为:

     # 基于类的用户登录path配置
    path('login/', LoginView.as_view(), name="login")
    

    你是不是觉得很方便了,不用判断请求的方法类型,直接就可以调用了!不过现在有个疑问,如果用户在提交表单的时候,都不满足我们表单的要求,比方说我们要求密码不得少于5位数,不能为空等,那样我们还需要用刚才的方法去验证么,其实根本就可以不用验证,这就是非法的字符。因此,我们有必要在表单提交时就定义一个可以检查是否需要继续后续操作的验证方法。

    我们继续往下进行:

    我们打开users这个应用,在里面新建forms.py文件,然后在里面输入如下代码:

    from django import forms
    
    
    # 用户登录表单的验证
    class LoginForm(forms.Form):
        username = forms.CharField(required=True)  # 用户名不能为空
        password = forms.CharField(required=True, min_length=5)  # 密码不能为空,而且最小6位数
    

    既然我们写好了表单的验证条件,接下来我们就把它用起来,继续回到我们的users/views.py文件,我们对登录函数做一个修改:

    #  基于类实现用户的登录,它需要继承view
    class LoginView(View):
        # 不需要判断,直接调用get方法,因为是获取信息,故这里不需要验证
        def get(self, request):
            # render的作用是渲染html并返回给用户
            # render三要素: request ,模板名称 ,一个字典用于传给前端并在页面显示
            return render(request, "login.html", {})
    
        # 不需要判断,直接调用post方法
        def post(self, request):
            # 类的实例化需要一个字典dict参数,而前面我们就知道request.POST是一个QueryDict,所以可以直接传入POST中的username,password等信息
            login_form = LoginForm(request.POST)
            # is_valid()方法,用来判断我们所填写的字段信息是否满足我们在LoginForm中所规定的要求,验证成功则继续进行,失败就跳回login页面并重新输入信息
            if login_form.is_valid():
                # username,password为前端页面name的返回值,取到用户名和密码我们就开始进行登录验证;取不到时为空。
                user_name = request.POST.get('username', '')
                pass_word = request.POST.get('password', '')
                # 取值成功返回user对象,失败返回null
                user = authenticate(username=user_name, password=pass_word)
    
                if user is not None:
                    # login 有两个参数:request和user。我们在请求的时候,request实际上是写进了一部分信息,然后在render的时候,这些信息也被返回前端页面从而完成用户登录
                    login(request, user)
                    # 页面跳转至网站首页 user request也会被带回到首页,显示登录状态
                    return render(request, "login.html")
                else:
                    # 说明里面的值是None,再次跳转回主页面并报错
                    return render(request, "login.html", {'msg': '用户名或者密码错误!'})
    
    
    

    现在我们又有一个疑问,假定你密码输入错误以后,你是不是只需要重新输入密码即可,用户名还是存在的,我们不希望还要去重新输入用户名。因此,我们的错误提示信息需要修改,继续完善我们的views.py文件。

     if user is not None:
                    # login 有两个参数:request和user。我们在请求的时候,request实际上是写进了一部分信息,然后在render的时候,这些信息也被返回前端页面从而完成用户登录
                    login(request, user)
                    # 页面跳转至网站首页 user request也会被带回到首页,显示登录状态
                    return render(request, "login.html")
                else:
                    # 说明里面的值是None,再次跳转回主页面并报错,这里仅当用户密码出错时才返回
                    return render(request, "login.html", {'msg': '用户名或者密码错误!'})
            # 所填写的字段信息不满足我们在LoginForm中所规定的要求,验证失败跳回login页面并重新输入信息
            else:
                return render(request, "login.html", {"login_form": login_form})
    
    
    

    就是下面这个样子:


    注意:forms.py文件中的username和password必须和html中的保持一致,因为它是我们从request.POST中得到的(request.POST也是从前端页面获取到的)。

    Debug测试一下

    我们在views.py文件的if login_form.is_valid():处打上一个断点,开启调式模式:
    我们不输入用户名,密码输ad这2个数,然后回车,页面进入Pycharm,单击一步步调试按钮,可以出现:


    放大一下:
    所以我们知道,这个errors其实是一个ErrorDict,我们可以通过这个来获取错误信息的提示,然后把它放在前端页面即可,还记得我们说过如果表单提交验证不通过直接刷新返回到登录页面,就是这段代码起的作用:
            else:
                return render(request, "login.html", {"login_form": login_form})
    

    现在我们在前端登录页面login.html配置错误提示信息:

    加上错误提示代码:

    <div class="form-group marb20 {% if login_form.errors.username %}errorput{% endif %}">
    
    <div class="form-group marb8 {% if login_form.errors.password %}errorput{% endif %}">
    
    就是这个样子:

    仅仅这样还不够,我们需要将forms.py文件里定义的错误信息给显示出来,那需要修改并添加一下代码:

    <div class="error btns login-form-tips" id="jsLoginTips">
                            {% for key,error in login_form.errors.items %}
                                {{ error }}
                            {% endfor %}
                            {{ msg }}
    </div>
    
    就是这个样子:
    我们现在运行一下我们的项目:runserver一下,不写任何信息:

    cookie和session自动登录

    cookie的用途

    正如你所知道的http请求是一种无状态的请求,也就是说每次请求是独立的。假如用户向服务器发起两次请求,那么这两次请求是没有状态的,服务器不知道这两次请求都是同一个用户发起的。

    所以为了解决这个无状态请求,人们引入了cookie这个概念,这使得每次请求变得有状态起来:

    记住cookie是不能跨域访问的!
    我们以chrome浏览器为例,打开百度的首页,并按键盘上的F12(或者ctrl+shift+I),开启监控模式:
    然后我们尝试清空一下cookies,重新刷新一下页面:
    cookie又出现了,所以cookie可以把你的用户名和密码保存在浏览器本地。

    不过这样做有个坏处,那就是你的密码和用户名都是缓存在本地的,一旦别人拿到你的电脑,那你的信息将被剽窃,非常不安全。

    那么我们在想有没有一种可以把数据缓存在服务器,然后发给用户的只是类似于钥匙的东西,而且这个钥匙最好是有过期时间,这样人家就算拿到你的电脑,也只是知道类似于钥匙的东西还必须在规定的时间内才能打开,这样一来安全得到了非常好的保证,session就是在满足这些条件下诞生的。

    session的安全护航

    用户在向服务器发起第一次请求后,服务器会给用户随机地分派一个id(可以是用户的user id,也可以一段任意的字符串),我们把它叫做session id。这个session id是服务器采用自己的规则生成,它保存在本地cookie里面。当用户再次发起请求时,这个session id会上传至服务器,服务器接收后会识别它,并返回相关的信息。

    我们登录的session就是这样:
    1、用户输入用户名 &和密码,点击提交;
    2、调用 login()命令, 后端程序会根据用户名密码生成session id并保存在数据库中;
    3、用户登录之后,需要通过这个session id取出相关的基本信息。

    我们打开我们的navicat,去我们的数据库中找到我们Django自带的session表:
    这个表中是登录后才有的,如果你此刻没有登录,请先登录一下我们的后台管理系统:
    你还可以清空我们这张表,然后重新登录试试,这里就不一一演示了(结果都是一样)。这个表中包含:session_key(钥匙), session_data(数据),expire_date(过期时间)。 我们运行一下我们的项目,用户先登录,然后我们按键盘上的F12(或者ctrl+shift+I),开启监控模式,查看cookie里面的信息是不是包含session id,而且主要检查数据是不是一样的:
    看到没有,和我们刚才数据库中看到的一模一样,不过session_key 在浏览器就叫做session id了,名字不同而已。这主要归功于django自带的session功能:

    如果你对cookie和session的原理感兴趣,可以浏览这篇文章:django从请求到返回都经历了什么?

    至此,本篇关于解决登录页面时的另一种验证方法,错误信息提示,cookie和session介绍这3个问题的介绍就到此结束了,感谢你的赏阅!

    本篇笔记对应于第十二篇代码,对应于github的位置是https://github.com/licheetools/eduline

    相关文章

      网友评论

        本文标题:零基础使用Django2.0.1打造在线教育网站(十二):错误信

        本文链接:https://www.haomeiwen.com/subject/ngwgmftx.html