zoukankan      html  css  js  c++  java
  • django+xadmin在线教育平台(十二)

    6-4 用form实现登录-1

    上面我们的用户登录的方法是基于函数来做的。本节我们做一个基于类方法的版本。
    要求对类的继承有了解。

    基础教程中基本上都是基于函数来做的,其实更推荐基于类来做。基于类可以带来不少好处

    # 基于类实现需要继承的view
    from django.views.generic.base import View
    class LoginView(View):
        # 直接调用get方法免去判断
        def get(self, request):
            # render就是渲染html返回用户
            # render三变量: request 模板名称 一个字典写明传给前端的值
            return render(request, "login.html", {})
        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)
    
            # 如果不是null说明验证成功
            if user is not None:
                # login_in 两参数:request, user
                # 实际是对request写了一部分东西进去,然后在render的时候:
                # request是要render回去的。这些信息也就随着返回浏览器。完成登录
                login(request, user)
                # 跳转到首页 user request会被带回到首页
                return render(request, "index.html")
            # 没有成功说明里面的值是None,并再次跳转回主页面
            else:
                return render(request, "login.html", {"msg": "用户名或密码错误! "})
    
     
    mark

    继承的view中的方法。

    django1.9.8 urls中的配置:

    # 换用类实现
    from users.views import LoginView
        # 基于类方法实现登录,这里是调用它的方法
        url('^login/$', LoginView.as_view(), name="login")
    

    Django2.0.1 urls配置:

        # 基于类方法实现登录,这里是调用它的方法
        path('login/', LoginView.as_view(), name="login")
    

    6-5 form字段验证

    验证最大长度,是否为空等一系列。

    users下新建forms文件。

    # encoding: utf-8
    __author__ = 'mtianyan'
    __date__ = '2018/1/10 0010 04:44'
    # 引入Django表单
    from  django import forms
    
    # 登录表单验证
    class LoginForm(forms.Form):
        # 用户名密码不能为空
        username = forms.CharField(required=True)
        password = forms.CharField(required=True, min_length=5)
    
    

    定义好forms之后我们来使用它做验证。

    def post(self, request):
            # 类实例化需要一个字典参数dict:request.POST就是一个QueryDict所以直接传入
            # POST中的usernamepassword,会对应到form中
            login_form = LoginForm(request.POST)
            #is_valid判断我们字段是否有错执行我们原有逻辑,验证失败跳回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)
    
                # 如果不是null说明验证成功
                if user is not None:
                    # login_in 两参数:request, user
                    # 实际是对request写了一部分东西进去,然后在render的时候:
                    # request是要render回去的。这些信息也就随着返回浏览器。完成登录
                    login(request, user)
                    # 跳转到首页 user request会被带回到首页
                    return render(request, "index.html")
            # 验证不成功跳回登录页面
            # 没有成功说明里面的值是None,并再次跳转回主页面
            else:
                return render(request, "login.html", {"msg": "用户名或密码错误! "})
    

    完善错误提示

    比如:既然表单都验证失败了,就不用显示密码出错了

     
    mark
    # 仅当用户真的密码出错时
                else:
                    return render(request, "login.html",{"msg":"用户名或密码错误!"})
            # 验证不成功跳回登录页面
            # 没有成功说明里面的值是None,并再次跳转回主页面
            else:
                return render(
                    request, "login.html", {
                        "login_form": login_form })
    

    forms中的名称username和password必须和html中的一致。毕竟他是使用的request.POST
    而request是从前面传过来的。

    实例化LoginView时已经对于我们的字段进行了验证。

    打上断点:

     
    mark

    debugf6运行到

     
    mark

    此时可以看到errors(ErrorDict)中的错误

     
    mark

    将form传回前端:

     
    mark

    前端中取值:

     
    mark

    给这个class加上errorput会显示红色外框。

     
    mark

    注意:写在class里面

    将forms错误信息显示出来

    <div class="error btns login-form-tips" id="jsLoginTips">
    {% for key, error in login_form.errors.items %}
    {{ error }}
    {% endfor %}
    {{ msg }}</div>
    
     
    mark
    • 写了一个类继承Django的view,然后写了get post方法(get/post的if是Django替我们完成的)
    • 在url中调用Loginview的as_view方法需要加上括号,进行调用。
    • Django的form进行表单验证并把error值传到前台。
    • is_valid方法,验证表单

    本小节完毕对应commit:

    6-4 & 5 登录换用类继承view实现,使用Django form进行表单验证并把错误信息提示到前台。

    6-6 session和cookie自动登录机制

    我们本节来讲session和cookie

    User1如何实现登录的。

    cookie的存储

    cookie是浏览器支持的一种本地存储方式。以dict,键值对方式存储。

    {"sessionkey": "123"}
    

    浏览器会自动对于它进行解析。

    http请求是一种无状态的请求

    用户向服务器发起的两次请求之间是没有状态的。也就是服务器并不知道这是同一个用户发的。

    做到记住用户:

    浏览器a在向服务器发起请求,服务器会自动给浏览器a回复一个id,浏览器a把id放到cookie当中,在下一次请求时带上这个cookie里的id值向浏览器请求,服务器就知道你是哪个浏览器发过来的了。

    有状态请求(cookie)

     
    mark

    服务器a发回来的id会放到服务器a的域之下。不能跨域访问cookie。

    使用浏览器随便打开一个网页,然后f12打开。

    比如我使用的Chrome浏览器

     
    mark

    会找到存储在浏览器本地的cookie值

     
    mark

    点击clear all清空所有的cookie f5刷新页面,会发现又把这些cookie值进来。

    如果将用户名和密码直接保存在cookie,可以实现最垃圾最简略版本的自动登录。

    解决cookie放在本地不安全的问题(session)

    用户在第一次请求后,浏览器回复的id既可以是用户的user id。
    也可以一段任意的字符串,我们把它叫做session id

    根据用户名和密码,服务器会采用自己的规则生成session id。这个session id保存在本地cookie。浏览器请求服务器会携带。

    • 输入用户名 & 密码
    • 调用 login(), 后端程序会根据用户名密码生成session id。保存在数据库中。
    • 用户登录之后,需要通过这个session id取出这些基本信息。
     
    mark

    Django的默认表中的session表就记录了用户登录时,后端我们Django为用户生成的sessionid

     
    mark

    可以看到session key value 和过期时间。

    我们可以清空这张表的数据。运行项目进行登录。

     
    mark

    可以看到我们刚刚生成的session id。

    此时通过f12查看浏览器在本地存储的session id。可以看到如下图和我们数据库中的一致。

     
    mark

    session_key 发到浏览器叫做session id

    通过session id 用户访问任何一个页面都会携带,服务器就会认识。

    Setting.py中,

     
    mark

    这个app会拦截我们每次的request请求,在request中找到session id,然后去数据表中进行查询。
    然后通过session key 去找到session data。此时直接为我们取出了user。

    在服务器返回浏览器的response中也会直接加上session id

    cookie是浏览器本地存储机制,存在域名之下,存储不安全。
    服务器在返回id时通过规则生成一串字符,并设置了过期时间。存储在服务器端(数据库)

    文章: http://projectsedu.com/2016/10/17/django%E4%BB%8E%E8%AF%B7%E6%B1%82%E5%88%B0%E8%BF%94%E5%9B%9E%E9%83%BD%E7%BB%8F%E5%8E%86%E4%BA%86%E4%BB%80%E4%B9%88/

    6-7 用户注册

    拷贝注册页面进入template目录

    书写我们对应要处理的view(RegisterView)

    users/views.py

    # 注册功能的view
    class RegisterView(View):
        # get方法直接返回页面
        def get(self, request):
            return render(request, "register.html", {})
    

    配置对应的url

    Django1.9.8 url配置如下:

    from users.views import RegisterView
        # 注册url
        url("^register/", RegisterView.as_view(), name="register"),
    

    Django2.0.1 url配置如下

    from users.views import RegisterView
        # 注册url
        path("register/", RegisterView.as_view(), name = "register" )
    

    修改index页面中注册url

     
    mark

    此时访问首页发现可以成功跳转到注册页面

    修改静态文件中static目录引用

    关键步骤load staticfile

     
    mark

    然后修改路径为一个相对于static的相对路径

     
    mark

    他会自动根据setting中配置,为我们加上前缀

     
    mark

    如果我们把目录在setting中改到mystatic。url中会自动添加指定的前缀

    可以看到可以访问成功。

     
    mark

    将目前的三个html中的静态文件全部修改目录

    枯燥但是要有耐心。

    这时候访问三个页面,查看样式是否完好。

    验证码库实现验证码

    https://github.com/mbi/django-simple-captcha

    安装配置

    workon mxonline3
    pip install  django-simple-captcha
    workon mxonline2
    pip install  django-simple-captcha==0.4.6
    
    • Add captcha to the INSTALLED_APPS in your settings.py

    • Add an entry to your urls.py:

    django1.9.8如下:

    from django.conf.urls import url, include
    urlpatterns += [
        url(r'^captcha/', include('captcha.urls')),
    ]
    

    django2.0.1如下;

        # 验证码url
        path("captcha/", include('captcha.urls'))
    
    makemigrations
    migrate
    
     
    mark

    进入数据库查看生成的表

     
    mark
     
    mark

    将验证码展示到页面

    users/forms.py:

    定义我们的register form:

    # 引入验证码field
    from captcha.fields import CaptchaField
    
    # 验证码form & 注册表单form
    class RegisterForm(forms.Form):
        # 此处email与前端name需保持一致。
        email = forms.EmailField(required=True)
        # 密码不能小于5位
        password = forms.CharField(required=True, min_length=5)
        # 应用验证码
        captcha = CaptchaField()
    
    

    users/views.py

    在我们的registerform中实例化并传送到前端:

    # form表单验证 & 验证码
    from .forms import LoginForm, RegisterForm
    
    # 注册功能的view
    class RegisterView(View):
        # get方法直接返回页面
        def get(self, request):
            # 添加验证码
            register_form = RegisterForm()
            return render(request, "register.html", {'register_form':register_form})
    
    

    前端获取验证码值

     
    mark
     
    mark

    找到上图验证码部分。修改为下图

     
    mark

    Forms中的field会生成不同的框。

     
    mark

    我们只有label但是前端可以查看到input框等,也就是Registerform会为我们生成输入框+验证码。

    隐藏的字符串的框会被带到后台,由Django为我们进行验证。验证该验证码是否保存过。

     
    mark

    可以看得我们数据库中将这个hashkey进行了保存。这个key与验证码内容对应。

    后台会把验证码值 和 hashkey进行联合查询。

    编写register view的后台逻辑(RegisterView)

    users/views.py的RegisterView中添加post方法:

        def post(self, request):
            # 实例化form
            register_form = RegisterForm(request.POST)
            if register_form.is_valid():
                pass
    
     
    mark
     
    mark

    修改form表单提交方式与提交到哪个url

     
    mark

    前端的form提交加上对应的crsf token

    刷新验证码是前端帮我们完成的:

    //刷新验证码
    function refresh_captcha(event){
        $.get("/captcha/refresh/?"+Math.random(), function(result){
            $('#'+event.data.form_id+' .captcha').attr("src",result.image_url);
            $('#id_captcha_0').attr("value",result.key);
        });
        return false;
    }
    

    获取前端页面值并封装成一个user_profile对象,保存到数据库。

    from django.contrib.auth.hashers import make_password
    
            if register_form.is_valid():
                user_name = request.POST.get("email", "")
                pass_word = request.POST.get("password", "")
    
                # 实例化一个user_profile对象,将前台值存入
                user_profile = UserProfile()
                user_profile.username = user_name
                user_profile.email = user_name
    
                # 加密password进行保存
                user_profile.password = make_password(pass_word)
                user_profile.save()
                pass
    
    

    发送邮件实现

    setting中配置;

    # 发送邮件的setting设置
    
    EMAIL_HOST = "smtp.qq.com"
    EMAIL_PORT = 25
    EMAIL_HOST_USER = "mxonline.mtianyan.cn"
    EMAIL_HOST_PASSWORD = " "
    EMAIL_USE_TLS= True
    EMAIL_FROM = "mxonline.mtianyan.cn"
    

    新建package后新建文件。

    apps:utils/email_send.py:

    # encoding: utf-8
    from random import Random
    
    __author__ = 'mtianyan'
    __date__ = '2018/1/10 0010 20:47'
    from  users.models import EmailVerifyRecord
    # 导入Django自带的邮件模块
    from django.core.mail import send_mail
    # 导入setting中发送邮件的配置
    from Mxonline2.settings import EMAIL_FROM
    
    # 生成随机字符串
    def random_str(random_length=8):
        str = ''
        # 生成字符串的可选字符串
        chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
        length = len(chars) - 1
        random = Random()
        for i in range(random_length):
            str += chars[random.randint(0, length)]
        return str
    
    # 发送注册邮件
    def send_register_eamil(email, send_type="register"):
        # 发送之前先保存到数据库,到时候查询链接是否存在
    
        # 实例化一个EmailVerifyRecord对象
        email_record = EmailVerifyRecord()
        # 生成随机的code放入链接
        code = random_str(16)
        email_record.code = code
        email_record.email = email
        email_record.send_type = send_type
    
        email_record.save()
    
        # 定义邮件内容:
        email_title = ""
        email_body = ""
    
        if send_type == "register":
            email_title = "mtianyan慕课小站 注册激活链接"
            email_body = "请点击下面的链接激活你的账号: http://127.0.0.1:8000/active/{0}".format(code)
    
            # 使用Django内置函数完成邮件发送。四个参数:主题,邮件内容,从哪里发,接受者list
            send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
            # 如果发送成功
            if send_status:
                pass
    
     
    mark

    上图为qq邮箱开启smtp服务

    点击生成授权码

     
    mark

    def post中加上发送邮件

    users/views.py

    # 发送邮件
    from utils.email_send import send_register_eamil
                # 发送注册激活邮件
                send_register_eamil(user_name, "register")
    

    点击注册提交,因为我们没有return。一直在转圈圈。

    但是数据库中已经添加了字段。

     
    mark

    可以看到我们的邮件已经被发送到邮箱中。

    如果注册成功返回login页面:不成功,返回register页面并报错。

    完善错误提示。

    找猫画虎:将login中的错误提示搬运到register中来。

    • register_form的报错信息。
     
    mark
     
    mark
    • 邮箱 & 密码 form验证
     
    mark

    完善用户值回填逻辑

     
    mark

    如果传回的有值则,显示传回来值。

    密码也做同样操作

    修改默认的激活状态为false

    post方法中

                # 默认激活状态为false
                user_profile.is_active = False
    

    书写处理激活的view。

    # 激活用户的view
    class ActiveUserView(View):
        def get(self, request, active_code):
            # 查询邮箱验证记录是否存在
            all_record = EmailVerifyRecord.objects.filter(code = active_code)
            # 激活form负责给激活跳转进来的人加验证码
            active_form = ActiveForm(request.GET)
            # 如果不为空也就是有用户
            if all_record:
                for record in all_record:
                    # 获取到对应的邮箱
                    email = record.email
                    # 查找到邮箱对应的user
                    user = UserProfile.objects.get(email=email)
                    user.is_active = True
                    user.save()
                    # 激活成功跳转到登录页面
                    return render(request, "login.html", )
            # 自己瞎输的验证码
            else:
                return render(request, "register.html", {"msg": "您的激活链接无效","active_form": active_form})
    
    

    配置用户激活的url并通过url提取到变量:

    django1.9.8:

        # 激活用户url
        url(r'^active/(?P<active_code>.*)/$',ActiveUserView.as_view(), name= "user_active")
    

    django2.0.1:

        # 激活用户url
        re_path('active/(?P<active_code>.*)/', ActiveUserView.as_view(), name= "user_active")
    

    这里通过?p将后面.*代表全部提取的正则,符合的内容传入参数active_code中/$代表以/$为结尾

     
    mark

    其他细节根据自己需要进行优化。

    注册功能制作完毕。对应commit:

    注册功能实现完毕,流程:注册,发邮件,激活,登录。对应6-6,7,8,9,10



    原文学习来自简书,作者:天涯明月笙
    链接:https://www.jianshu.com/p/9c621518d991

  • 相关阅读:
    HTTP协议超级详解
    css面试题(一)
    win10开机启动项管理
    Notion快捷键
    [C++]std::sort()函数使用总结
    配置环境变量时java命令可以使用,javac不能使用
    安卓4:修改系统默认颜色
    vue 安装sass-loader 提示python等
    vue echarts 柱状图 变色 显示
    k8s-生产环境部署django项目k8s-dashboard管理系统
  • 原文地址:https://www.cnblogs.com/xinjie57/p/9232627.html
Copyright © 2011-2022 走看看