07登录登出

用户登录功能实现

分析

  • 业务处理流程
    • 判断用户输入的账号是否为空
    • 判断用户输入的密码是否为空
    • 判断用户输入的账号与密码是否正确
  • 请求方法:post
  • url定义:/users/login/
  • 请求参数:url路径参数
参数类型前端是否必须传描述
user_account字符串用户输入的账号可以是手机号也可以是用户名
password字符串用户输入的密码
remember_me字符串用户输入的“是否记住我”

由于是后端请求,在向后端发起请求时,需要附带csrf_token

用户登出功能实现

分析

  • 请求方法:get
  • url定义:/users/logout/
  • 实现:调用django自带的logout(request)函数即可

superuser

  • 创建管理员账号:python manage.py createsuperuser,然后填用户名,手机号,密码
    在这里插入图片描述

前端代码

apps/users/views.py

from django.shortcuts import render,redirect,reverse
from django.views import View #导入类视图
from utils.json_fun import to_json_data
from utils.res_code import Code,error_map
from apps.users.forms import RegisterForm,LoginForm
from apps.users.models import Users
from django.contrib.auth import login,logout
import json

#用到渲染模板和提交数据用类视图比较多

class RegisterView(View):
    """
    #一般会注释url
    /users/register
    """
    def get(self,request):
        return render(request,'users/register.html')

#需要的字段:用户名,密码,确认密码,手机号,短信验证码
#请求方式post
#提交: form表单,ajax
#获取,验证
#步骤:
# 1.获取参数
# 2.校验参数
# 3.保存参数到数据库
# 4.返回给前端
    #1.获取参数
    def post(self,request):
        json_data = request.body    #从前端收到的json类型数据
        if not json_data:
            return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])

        dict_data = json.loads(json_data.decode('utf8'))    #将json类型转成字典
        #2.校验参数,用form表单方式校验
        form = RegisterForm(data=dict_data)
        if form.is_valid():
            #3.保存数据到数据库
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            mobile = form.cleaned_data.get('mobile')

            user = Users.objects.create_user(username=username,password=password,mobile=mobile)
            login(request,user)
            #4.返回给前端
            return to_json_data(errmsg='恭喜您,注册成功')

        else:
            #定义一个错误信息列表
            err_msg_list = []
            for item in form.errors.get_json_data().values():
                err_msg_list.append(item[0].get('message'))

            err_msg_str = '/'.join(err_msg_list)    #拼接错误信息为一个字符串
            return to_json_data(errno=Code.PARAMERR,errmsg=err_msg_str)

#传给后台的参数:登录账号,密码,记住我
#请求方式:POST
#提交:ajax
class LoginView(View):
    """
    /users/login/
    """
    def get(self,request):
        return render(request,'users/login.html')

    def post(self,request):
        #1.获取参数
        json_data = request.body    #获取的是bytes格式
        if not json_data:
            return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])

        dict_data = json.loads(json_data.decode('utf8'))    #或者'utf-8'  将bytes格式解码成字符串,再转成json格式

        #2.校验:账号,账号格式,是否为空,账号和密码,数据库
        form = LoginForm(data=dict_data,request=request)    #类的实例化

        #3.返回给前端
        if form.is_valid():
            return to_json_data(errmsg='恭喜您!登陆成功!')
        else:
            #定义一个错误信息列表
            err_msg_list = []
            for item in form.errors.get_json_data().values():
                err_msg_list.append(item[0].get('message'))

            err_msg_str = '/'.join(err_msg_list)    #拼接错误信息为一个字符串
            return to_json_data(errno=Code.PARAMERR,errmsg=err_msg_str)


class LogoutView(View):
    """
	/users/logout/
    """
    def get(self,request):
        logout(request) #删除redis数据库中的session  数据库和浏览器存在相同的session_id,证明这个账号处于登录状态
        return redirect(reverse('users:login'))

base.html

{% load static %}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  {% block meta %}{% endblock %}
  {% block title %}{% endblock %}
  <link rel="stylesheet" href="{% static 'css/base/reset.css' %}">
  <link rel="stylesheet" href="{% static 'css/base/common.css' %}">
  {% block link %}{% endblock %}
  <link rel="stylesheet" href="{% static 'css/base/side.css' %}">
  <link rel="stylesheet" href="http://at.alicdn.com/t/font_684044_un7umbuwwfp.css">
</head>
<body>
<!-- header start -->
<header id="header">
  <div class="mw1200 header-contain clearfix">
    <!-- logo start -->
    <h1 class="logo">
      <a href="javascript:void(0);" class="logo-title">Python</a>
    </h1>
    <!-- logo end -->
    <!-- nav start -->
    <nav class="nav">
      <ul class="menu">
        <li><a href="{% url 'news:index' %}">首页</a></li>
        <li><a href="{% url 'course:course' %}">在线课堂</a></li>
        <li><a href="{% url 'doc:docDownload' %}">下载文档</a></li>
        <li><a href="{% url 'news:search' %}">搜索</a></li>
      </ul>
    </nav>
    <!-- nav end -->
    <!-- login start -->
      <div class="login-box">
        {% if user.is_authenticated %}
          <div class="author">
              <i class="PyWhich py-user"></i>
              <span>{{ user.username }}</span>
              <ul class="author-menu">
                {% if user.is_staff %}
                  <li><a href="javascript:void(0);">后台管理</a></li>
                {% endif %}
                  <li><a href="{% url 'users:logout' %}">退出登录</a></li>
              </ul>
          </div>
        {% else %}
          <div>
              <i class="PyWhich py-user"></i>
              <span>
{#                  <a href="{% url 'users:login' %}" class="login">登录</a> / <a href="{% url 'users:register' %}"#}
                  <a href="{% url 'users:login' %}" class="login">登录</a> / <a href="{% url 'users:register' %}"
                                                                          class="reg">注册</a>
              </span>
          </div>
        {% endif %}
{#          <div class="author hide">#}
{#              <i class="PyWhich py-user"></i>#}
{#              <span>qwertyui</span>#}
{#              <ul class="author-menu">#}
{#                  <li><a href="javascript:void(0);">后台管理</a></li>#}
{#                  <li><a href="javascript:void(0);">退出登录</a></li>#}
{#              </ul>#}
{#          </div>#}
      </div>
    <!-- login end -->
  </div>
</header>
<!-- header end -->

<!-- main start -->
{% block main %}
<main id="main">
  <div class="w1200 clearfix">
    <!-- main-contain start  -->
    {% block contain %}
    {% endblock %}
    <!-- main-contain  end -->
    <!-- side start -->
  {% block side %}
      <aside class="side">
          <div class="side-activities">
              <h3 class="activities-title">在线课堂<a href="javascript:void(0)">更多</a></h3>
              <div class="activities-img">
                  <a href="javascript:void(0);" target="_blank">
                      <img src="{% static 'img/english.jpg' %}" alt="title">
                  </a>
                  <p class="activities-tips">对话国外小姐姐</p>
              </div>
              <ul class="activities-list">
                  <li>
                      <a href="javascript:void(0);" target="_blank">
                          <span class="active-status active-start">报名中</span>
                          <span class="active-title"><a
                                  href="https://www.shiguangkey.com/course/2432"> Django 项目班</a></span>
                      </a>
                  </li>
                  <li>
                      <a href="javascript:void(0);" target="_blank">
                          <span class="active-status active-end">已结束</span>
                          <span class="active-title"><a
                                  href="https://www.shiguangkey.com/course/2321">Python入门基础班</a></span>
                      </a>
                  </li>
              </ul>
          </div>
          <div class="side-attention clearfix">
              <h3 class="attention-title">关注我</h3>
              <ul class="side-attention-address">
                  <li>
                      <a href="javascript:void(0);" target="_blank"><i class="PyWhich py-GitHub"></i>Taka</a>
                  </li>
                  <li>
                      <a href="javascript:void(0);" target="_blank"><i class="PyWhich py-zhihu"
                                                                       style="color:rgb(0, 108, 226);"></i>Taka</a>
                  </li>
                  <li>
                      <a href="javascript:void(0);" target="_blank"><i class="PyWhich py-weibo"
                                                                       style="color:rgb(245,92,110);"></i>Taka</a>
                  </li>
              </ul>
              <div class="side-attention-qr">
                  <p>扫码关注</p>
              </div>
          </div>

          <div class="side-hot-recommend">
              <h3 class="hot-recommend">热门推荐</h3>
              <ul class="hot-news-list">

                  <li>
                      <a href="javascript:void(0)" class="hot-news-contain clearfix">
                          <div class="hot-news-thumbnail">
                              <img src="{% static 'img/python_web.jpg' %}"
                                   alt="">
                          </div>
                          <div class="hot-news-content">
                              <p class="hot-news-title">Django调试工具django-debug-toolbar安装使用教程</p>
                              <div class="hot-news-other clearfix">
                                  <span class="news-type">python框架</span>
                                  <!-- 自带的 -->
                                  <time class="news-pub-time">11月11日</time>
                                  <span class="news-author">python</span>
                              </div>
                          </div>
                      </a>
                  </li>

              </ul>
          </div>


      </aside>
  {% endblock %}
    <!-- side end -->
  </div>
</main>
{% endblock %}
<!-- main end -->

<!-- footer start -->
<footer id="footer">
    <div class="footer-box">
        <div class="footer-content">
            <p class="top-content">
					<span class="link">
						<a href="javascript:void(0)">关于Python</a> |
						<a href="javascript:void(0)">我就是我</a> |
						<a href="javascript:void(0)">人生苦短</a> |
						<a href="javascript:void(0)">我用Python</a>
					</span>
                <span class="about-me">关于我: <i class="PyWhich py-wechat"></i> Taka</span>
            </p>
            <p class="bottom-content">
                <span>地址: xxxx</span>
                <span>联系方式: <a href="tel:400-1567-315">400-1567-315</a> (24小时在线)</span>
            </p>
        </div>
        <p class="copyright-desc">
            Copyright &copy; 2008 - 2019 xxx有限公司. All Rights Reserved
        </p>
    </div>
</footer>
<!-- footer end -->
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
{% block domready %}{% endblock %}
</body>
</html>
  • settings.py中设置session默认时间:3天SESSION_COOKIE_AGE = 3 * 24 * 60 * 60

后端代码

apps/users/forms.py

#!/home/xiaoge/env python3.6
# -*- coding: utf-8 -*-
"""
  __title__ = ' forms'
  __author__ = 'xiaoge'
  __mtime__ = '2019/5/29 上午10:37'
  # code is far away from bugs with the god animal protecting
      I love animals. They taste delicious.
               ┏┓      ┏┓
              ┏┛┻━━━━━━┛┻┓
              ┃        ☃ ┃
              ┃  ┳┛ ┗┳   ┃
              ┃    ┻     ┃
              ┗━┓      ┏━┛
                ┃      ┗━━━┓
                ┃ 神兽保佑  ┣┓
                ┃永无BUG!┏┛
                ┗┓┓┏━┳┓┏┛
                ┃┫┫  ┃┫┫
               ┗┻┛  ┗┻┛
"""

import re

from django import forms
from django_redis import get_redis_connection
from django.db.models import Q
from django.contrib.auth import login

from apps.verifications.constants import SMS_CODE_NUMS
from apps.users.models import Users
from apps.users import constants


class RegisterForm(forms.Form):
    """
    #1.单个字段格式验证
    # 2.手机号,格式,是否已注册
    # 3.密码是否一致
    # 4.短信验证码是否跟数据不一致
    """
    #1.单个字段格式验证
    username = forms.CharField(label='用户名',
                               max_length=20,
                               min_length=5,
                               error_messages={'min_length':'用户名长度要大于5',
                                               'max_length':'用户名长度要小于20',
                                               'required':'用户名不能为空'}
                               )
    password = forms.CharField(label='密码',
                               max_length=20,
                               min_length=6,
                               error_messages={'min_length':'密码长度要大于6',
                                               'max_length':'密码长度要小于20',
                                               'required':'密码不能为空'}
                               )
    password_repeat = forms.CharField(label='确认密码',
                                      max_length=20,
                                      min_length=6,
                                      error_messages={'min_length':'密码长度要大于6',
                                                      'max_length':'密码长度要小于20',
                                                      'required':'确认密码不能为空'}
                                      )
    mobile = forms.CharField(label='手机号',
                             max_length=11,
                             min_length=11,
                             error_messages={'min_length':'手机号长度有误',
                                             'max_length':'手机号长度有误',
                                             'required':'手机号不能为空'}
                             )
    sms_code = forms.CharField(label='短信验证码',
                               max_length=SMS_CODE_NUMS,
                               min_length=SMS_CODE_NUMS,
                               error_messages={'min_length':'短信验证码长度有误',
                                               'max_length':'短信验证码长度有误',
                                               'required':'短信验证码不能为空'}
                               )

    def clean_mobile(self):
        """
        # 2.手机号,格式,是否已注册
        :return:
        """
        tel = self.cleaned_data.get('mobile')   #从前端获取
        #验证输入的手机号格式
        if not re.match(r'^1[3-9]\d{9}$',tel):  #效果同verifications的forms.py中的RegexValidator
            raise forms.ValidationError('手机号码格式不正确')
        #验证输入的手机号在数据库中是否存在
        if Users.objects.filter(mobile=tel).exists():
            raise forms.ValidationError('手机号已注册!请重新输入!')

        return tel

    def clean(self):
        """
        # 3.密码是否一致
        # 4.短信验证码是否跟数据不一致
        :return:
        """
        #cleaned_data 就是读取前端form表单返回的值,返回类型为字典dict型
        cleaned_data = super().clean()  #继承clean的方法,重写
        # 3.密码是否一致
        password = cleaned_data.get('password')     #从那个字典中获取键password对应的值
        password_repeat = cleaned_data.get('password_repeat')
        if password != password_repeat: #判断密码和确认密码是否一致
            raise forms.ValidationError('两次输入密码不一致')

        sms_text = cleaned_data.get('sms_code')
        tel = cleaned_data.get('mobile')
        # 4.短信验证码是否跟数据不一致
        #连接redis数据库
        redis_conn = get_redis_connection('verity_codes')
        #构建短信验证码
        sms_fmt = 'sms_{}'.format(tel)
        #获取数据库中cleaned_data的短信验证码
        real_sms = redis_conn.get(sms_fmt)
        if (not real_sms) or (sms_text != real_sms.decode('utf-8')):    #判断结果为总是抛出异常
            raise forms.ValidationError('短信验证码错误')


class LoginForm(forms.Form):
    """

    """
    user_account = forms.CharField()    #这三个字段对应login.js中传入后端的字段
    password = forms.CharField(label='密码',
                               max_length=20,
                               min_length=6,
                               error_messages={'min_length':'密码长度要大于6', #该参数是继承CharField,CharField又继承Field的error_messages
                                              'max_length':'密码长度要小于20',
                                              'required':'密码不能为空'}
                               )
    remember_me = forms.BooleanField(required=False)    #默认不记住

    def __init__(self,*args,**kwargs):
        # 字典方法,如果没有request这个键,返回None给self.request
        #如果有request这个键,返回对应的值给self.request
        self.request = kwargs.pop('request',None)
        super().__init__(*args,**kwargs)    #?
        # super().__init__()    #?

    def clean_user_account(self):
        """
        单字段验证
        拼接字段clean_字段名
        :return:
        """
        user_info = self.cleaned_data.get('user_account')   #cleaned_data是继承Form的方法,用于获取数据
        if not user_info:
            raise forms.ValidationError('用户账号不能为空!')
        #这个验证不好,要改-----------------------------------------------------------------------------
        if not re.match(r'^1[3-9]\d{9}$',user_info) and (len(user_info)<5 or len(user_info)>20):
            raise forms.ValidationError('输入的账号格式不正确!')

        return user_info

    def clean(self):
        cleaned_data = super().clean()  #先继承,再重写

        user_info = cleaned_data.get('user_account')    #这三个用户输入的字段尽量不和前面的重名
        passwd = cleaned_data.get('password')   #该密码是从前端获得,是明文的
        hold_login = cleaned_data.get('remember_me')
        #1.数据库
        #多条件的or连接,用到Q对象,django.db.models.Q
        #要么用户名是账号,要么用户名是手机号
        user_queryset = Users.objects.filter(Q(username=user_info) | Q(mobile=user_info))
        if user_queryset:
            user = user_queryset.first()    #从queryset中拿到第一个对象,该情况下也只有一个对象
            if user.check_password(passwd): #判断密码是否正确
                #2.登录的操作,remember_me(记住session)

                # 设置session过期时间
                if not hold_login:
                    self.request.session.set_expiry(None)
                else:
                    self.request.session.set_expiry(constants.USER_SESSION_EXPIRES)
                #login(request,用户实例)
                # request.session['username'] = username
                #login()需要一个HttpRequest对象和一个 User对象,login()使用Django的会话框架将用户的ID保存在会话中
                login(self.request,user)    #login第一个参数是request,第二个参数是登录的用户的实例
            else:
                raise forms.ValidationError('密码不正确,请重新输入!')
        else:
            raise forms.ValidationError('用户账号不存在,请重新输入!')

别忘了配置login/logout路由

apps/users/constants.py

  • 用于保存users下的文件中设置的常量
    USER_SESSION_EXPIRES = 14 * 24 * 60 *60 #设置session保存时长为十四天
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值