美文网首页
Django+Xadmin打造在线教育系统(三)

Django+Xadmin打造在线教育系统(三)

作者: 听你讲故事啊 | 来源:发表于2018-09-17 20:33 被阅读0次

    完成登录 注册 找回密码 激活 验证码集成

    将HTML文件拷贝到templates目录下,css,js,img,media,images文件夹拷贝到static文件夹下

    QQ截图20180907104837.png

    修改index.html和login.html文件中的静态文件路径,全部替换为如下格式

        <link rel="stylesheet" type="text/css" href="{% static "css/reset.css" %}">
        <link rel="stylesheet" type="text/css" href="{% static "css/animate.css" %}">
        <link rel="stylesheet" type="text/css" href="{% static "css/style.css" %}">
    

    在文件最上方加入

    {% load staticfiles %}
    

    配置主页面的路由

    urlpatterns = [
        path('admin/', xadmin.site.urls),
        path('',TemplateView.as_view(template_name='index.html'), name='index'),
    ]
    

    配置登陆页面的路由

    path('login/',LoginView.as_view(),name = 'login'),
    

    编写登陆的逻辑代码

    users/views.pu

    # 实现用户名邮箱均可登录
    # 继承ModelBackend类,因为它有方法authenticate,可点进源码查看
    from users.models import UserProfile
    
    
    class CustomBackend(ModelBackend):
        def authenticate(self, username=None, password=None, **kwargs):
            try:
                # 不希望用户存在两个,get只能有一个。两个是get失败的一种原因 Q为使用并集查询
    
                user = UserProfile.objects.get(Q(username=username)|Q(email=username))
    
                # django的后台中密码加密:所以不能password==password
                # UserProfile继承的AbstractUser中有def check_password(self, raw_password):
    
                if user.check_password(password):
                    return user
            except Exception as e:
                return None
    
    
    class LoginView(View):
        def get(self,request):
            return render(request, 'login.html')
    
        def post(self,request):
            # 获取用户提交的用户名和密码
            user_name = request.POST.get('username', None)
            pass_word = request.POST.get('password', None)
            # 成功返回user对象,失败None
            user = authenticate(username=user_name, password=pass_word)
            # 如果不是null说明验证成功
            if user is not None:
                # 登录
                login(request, user)
                return render(request, 'index.html')
            else:
                return render(request, 'login.html', {'msg': '用户名或密码错误'})
    

    settings.py中启用自定义验证

    # 自定义登陆验证
    AUTHENTICATION_BACKENDS = (
        'users.views.CustomBackend',
    )
    

    修改login文件中的form表单

    QQ截图20180907113957.png

    访问http://127.0.0.1:8000/login/即可进行登陆,成功会跳转到主页面,失败则会显示错误信息

    QQ截图20180907142801.png

    form实现登陆

    users目录下新建一个forms.py的文件

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

    修改LoginView的内容

        def post(self, request):
            # 类实例化需要一个字典参数dict:request.POST就是一个QueryDict所以直接传入
            # POST中的username,password,会对应到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:
                    # 账号是否激活
                    if user.is_active:
                        # login 两参数:request, user
                        # 实际是对request写了一部分东西进去,然后在render的时候:
                        # request是要render回去的。这些信息也就随着返回浏览器。完成登录
                        login(request, user)
                        # 跳转到首页 user request会被带回到首页
                        return render(request, "index.html")
                    else:
                        return render(request, "login.html", {"msg": "用户未激活!"})
                # 验证不成功跳回登录页面
                # 没有成功说明里面的值是None,并再次跳转回主页面
                else:
                    return render(request, "login.html", {"msg": "用户名或密码错误! "})
            # form.is_valid()已经判断不合法了,所以这里不需要再返回错误信息到前端了,错误信息包含在form.errors中
            else:
                return render(request,'login.html',{'login_form':login_form})
    

    修改login.html文件内容

    QQ截图20180907195000.png
                        <div class="form-group marb20 {% if login_form.errors.username %}errorput{% endif %}">
                            <label>用&nbsp;户&nbsp;名</label>
                            <input name="username" id="account_l" type="text" placeholder="手机号/邮箱" />
                        </div>
                        <div class="form-group marb8 {% if login_form.errors.username %}errorput{% endif %}">
                            <label>密&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;码</label>
                            <input name="password" id="password_l" type="password" placeholder="请输入您的密码" />
                        </div>
                        <div class="error btns login-form-tips" id="jsLoginTips">
                            {% for key,error in login_form.errors.items %}
                                {{ error }}
                            {% endfor %}
                            {{ msg }}
                        </div>
    

    使用admin账号进行登陆测试


    邮箱注册

    注册时需要验证码
    这里使用插件 django-simple-captcha

    pip install  django-simple-captcha
    

    配置到INSTALLED_APPS

    INSTALLED_APPS = [
        ...,
        'captcha',
    ]
    

    配置路由

    path('captcha/',include('captcha.urls')),
    

    映射到数据库

    python manage.py makemigrations
    
    python manage.py migrate
    

    创建一个注册的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(error_messages={'invalid': '验证码错误'})
    

    编写注册的逻辑代码

    class RegisterView(View):
        '''用户注册'''
        def get(self,request):
            register_form = RegisterForm()
            return render(request,'register.html',{'register_form':register_form})
    
        def post(self,request):
            register_form = RegisterForm(request.POST)
            if register_form.is_valid():
                user_name = request.POST.get('email', None)
                # 如果用户已存在,则提示错误信息
                if UserProfile.objects.filter(email = user_name):
                    return render(request, 'register.html', {'register_form':register_form,'msg': '用户已存在'})
    
                pass_word = request.POST.get('password', None)
                # 实例化一个user_profile对象
                user_profile = UserProfile()
                user_profile.username = user_name
                user_profile.email = user_name
                user_profile.is_active = False
                # 对保存到数据库的密码加密
                user_profile.password = make_password(pass_word)
                user_profile.save()
                send_register_eamil(user_name,'register')
                return render(request,'login.html',{"msg":"邮件已发送,进入邮箱激活"})
            else:
                return render(request,'register.html',{'register_form':register_form})
    

    修改register.html文件

    首先修改静态文件路径,接着修改表单配置


    TIM截图20180908113139.png
    
        <form id="email_register_form" method="post" action="/register/" autocomplete="off">
            <div class="form-group marb20 {% if register_form.errors.email %}errorput{% endif %}">
                <label>邮&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;箱</label>
                <input  type="text" id="id_email" name="email" value="{{ register_form.email.value|default:"" }}" placeholder="请输入您的邮箱地址" />
            </div>
             <div class="form-group marb8 {% if register_form.errors.password %}errorput{% endif %}">
                <label>密&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;码</label>
                <input type="password" id="id_password" name="password" placeholder="请输入6-20位非中文字符密码" />
            </div>
            <div class="form-group marb8 captcha1 {% if register_form.errors.captcha %}errorput{% endif %}">
                <label>验&nbsp;证&nbsp;码</label>
                {{ register_form.captcha }}
            </div>
            <div class="error btns" id="jsEmailTips">
                {% for key,error in register_form.errors.items %}
                    {{ error }}
                {% endfor %}
                {{ msg }}
            </div>
            <div class="auto-box marb8">
            </div>
            <input class="btn btn-green" id="jsEmailRegBtn" type="submit" value="注册并登录" />
        {% csrf_token %}
        </form>
    

    刷新验证码

    刷新验证码的功能是前端完成的

    //刷新验证码
    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;
    }
    

    发送邮件

    这里使用新浪邮箱,首先注册一个新浪邮箱,开启SMTP服务


    TIM截图20180908142647.png TIM截图20180908142751.png

    在apps目录下新建一个utils包,在这个包里面新建一个email_send.py文件

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 2018/9/7 下午 08:24
    # @Author  : gao
    # @File    : email_send.py
    
    from random import Random
    
    from django.core.mail import send_mail
    
    from MxOnline.settings import EMAIL_FROM
    from users.models import EmailVerifyRecord
    
    
    # 生成随机字符串
    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 = "慕课在线 注册激活链接"
            email_body = "请点击下面的链接激活你的账号: http://127.0.0.1:8000/active/{0}".format(code)
    
            # 使用Django内置函数完成邮件发送。四个参数:主题,邮件内容,发件人邮箱地址,收件人(是一个字符串列表)
            send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
            # 如果发送成功
            if send_status:
                pass
    
        if send_type == "forget":
            email_title = "慕课在线 找回密码链接"
            email_body = "请点击下面的链接找回你的密码: http://127.0.0.1:8000/reset/{0}".format(code)
    
            # 使用Django内置函数完成邮件发送。四个参数:主题,邮件内容,从哪里发,接受者list
            send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
            # 如果发送成功
            if send_status:
                pass
    
    

    邮箱激活

    # 激活用户的view
    class ActiveUserView(View):
        def get(self, request, active_code):
            # 查询邮箱验证记录是否存在
            all_record = EmailVerifyRecord.objects.filter(code = active_code)
            # 如果不为空也就是有用户
            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, "active_fail.html")
    

    在templates目录下创建 active_fail.html,代码如下:

        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Title</title>
        </head>
        <body>
            <p style="color: red;">链接失效</p>
        </body>
        </html>
    

    添加激活邮箱的路由

    re_path('active/(?P<active_code>.*)/',ActiveUserView.as_view(),name='user_active'),
    

    忘记密码

    点击忘记密码,跳转到忘记密码界面,输入邮箱和验证码后给该邮箱发送重置密码链接
    邮箱收到链接后,点击链接跳转到重置密码界面,输入新密码点击提交,成功后跳转到登陆界面

    路由设置

    path('forget/',ForgetPwdView.as_view(),name='forget_pwd'),
    

    新建一个form

    class ForgetPwdForm(forms.Form):
        '''忘记密码'''
        email = forms.EmailField(required=True)
        captcha = CaptchaField(error_messages={'invalid': '验证码错误'})
    

    逻辑代码

    class ForgetPwdView(View):
        '''找回密码'''
        def get(self,request):
            forget_form = ForgetPwdForm()
            return render(request,'forgetpwd.html',{'forget_form':forget_form})
    
        # post方法实现
        def post(self, request):
            forget_form = ForgetPwdForm(request.POST)
            # form验证合法情况下取出email
            if forget_form.is_valid():
                email = request.POST.get("email", "")
                # 发送找回密码邮件
                send_register_eamil(email, "forget")
                # 发送完毕返回登录页面并显示发送邮件成功。
                return render(request, "login.html", {"msg": "重置密码邮件已发送,请注意查收"})
            # 如果表单验证失败也就是他验证码输错等。
            else:
                return render(request, "forgetpwd.html", {"forget_from": forget_form})
    

    修改forgetpwd.html表单提交内容

        <form id="jsFindPwdForm" method="post" action="/forget/" autocomplete="off">
    
            <div class="form-group marb20 ">
                <label>帐&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;号</label>
                <input type="text" id="account" name="email" placeholder="邮箱" />
            </div>
            <div class="form-group captcha1 marb38">
                <label>验&nbsp;证&nbsp;码</label>
                {{ forget_form.captcha }}
            </div>
            <div class="error btns" id="jsForgetTips">
                {% for key,error in forget_from.errors.items %}
                    {{ error }}
                {% endfor %}
                {{ msg }}
            </div>
            <input type="hidden" name="sms_type" value="1">
            <input class="btn btn-green" id="jsFindPwdBtn" type="submit" value="提交" />
            <p class="form-p" style="bottom:40px;">您还可以<a href="/login/"> [直接登录]</a></p>
            {% csrf_token %}
        </form>
    

    密码重置

    配置路由

    re_path('reset/(?P<active_code>.*)/', ResetView.as_view(), name='reset_pwd'),
    

    逻辑代码

        # 重置密码的view
        class ResetView(View):
            def get(self, request, active_code):
                # 查询邮箱验证记录是否存在
                all_record = EmailVerifyRecord.objects.filter(code=active_code)
                # 如果不为空也就是有用户
                if all_record:
                    for record in all_record:
                        # 获取到对应的邮箱
                        email = record.email
                        # 将email传回来
                        return render(request, "password_reset.html", {"email":email})
                # 自己瞎输的验证码
                else:
                    return render(
                        request, "forgetpwd.html", {
                            "msg": "您的重置密码链接无效,请重新请求"})
    

    创建修改密码的form表单

    # 重置密码form实现
    class ModifyPwdForm(forms.Form):
        # 密码不能小于5位
        password1 = forms.CharField(required=True, min_length=5)
        # 密码不能小于5位
        password2 = forms.CharField(required=True, min_length=5)
    

    配置路由

    path('modify_pwd/', ModifyPwdView.as_view(), name='modify_pwd'),
    

    逻辑代码

     # 修改密码
    class ModifyPwdView(View):
        def post(self, request):
            modiypwd_form = ModifyPwdForm(request.POST)
            if modiypwd_form.is_valid():
                pwd1 = request.POST.get("password1", "")
                pwd2 = request.POST.get("password2", "")
                email = request.POST.get("email", "")
                # 如果两次密码不相等,返回错误信息
                if pwd1 != pwd2:
                    return render(request, "password_reset.html", {"email": email, "msg": "密码不一致"})
                # 如果密码一致
                user = UserProfile.objects.get(email=email)
                # 加密成密文
                user.password = make_password(pwd2)
                # save保存到数据库
                user.save()
                return render(request, "login.html", {"msg": "密码修改成功,请登录"})
            # 验证失败说明密码位数不够。
            else:
                email = request.POST.get("email", "")
                return render(request, "password_reset.html", {"email": email, "modiypwd_form":modiypwd_form})
    

    需要两个类是因为get请求和post请求参数不同
    html文件根据以前的方法进行配置即可

    相关文章

      网友评论

          本文标题:Django+Xadmin打造在线教育系统(三)

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