购物车案例——注册功能

python知识点回顾

  • 路由匹配可变的字符串
	  path('check/username/<str:username>/', CheckUsername.as_view())
  • 正则匹配
     In [2]: import re
     In [3]: a = re.findall(r'^[a-zA-Z]*(?P<num>\d+)[a-zA-Z]*$', 'sad131231dasda')
    
     In [4]: a
     Out[4]: ['131231']
    
     In [4]: mobile = re.findall(r"1\d{10}", '我的手机号是8615239159680')
     Out[4]: ['15239159680']
  • True 与 False的判断
 		# []、()、{}、""、 None、0、作为判断条件是均为False
    	if not re.findall(r"2\d{10}", '我的手机号是8615239159680'):
        # 代表没有匹配到
  • 模型类查询
    #查询用户对象
    user = User.objects.get(username=username)
    #查询集合
    user_query_set = User.objects.filter(username=username)

注册逻辑

  1. 生成页面的图片验证码,需要两种触发方式: 默认执行,点击执行
  2. 获取输入的用户名
    1). 利用正则校验用户名的合法性
    2). 当用户名正则校验通过,向 后端 发送请求,校验 用户名是否重复
  3. 获取密码
    1. 利用正则校验密码的合法性
  4. 获取确认密码
    1. 校验 确认密码和密码 是否一致
  5. 获取手机号
    1. 利用正则校验手机号的合法性
    2. 当手机号正则校验通过,向 后端 发送请求,校验手机号是否重复
  6. 获取用户输入的验证码
    1. 校验验证码是否存在
  7. 校验是否选择复选框,必须勾选
  8. 点击注册按钮,将信息提交到后端

用户表设计

用户表: 用户名、密码、手机号

可以自定义模型类,包含这些字段,在真正的项目开发中,不会自己重写一遍,而是继承 django内置的用户模型类: AbstractUser, 重写自己的字段, 但是需要在 settings.py 中 指明当前的用户模型类

from django.db import models
from django.contrib.auth.models import AbstractUser


# Create your models here.
class User(AbstractUser):
    mobile = models.CharField(max_length=11, verbose_name='手机号')

    class Meta:
        db_table = 'user_tb'
        verbose_name = '用户信息表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.username

在配置文件中指明使用的模型类

# 指定用户模型类
AUTH_USER_MODEL = 'users.User'  # app.模型类

迁移模型类

python manage.py makemigrations users
python manage.py migrate users

用户名重复性校验

== API设计 ==

	# 用户名传递问题
	GET /users/check/username/xxx/

== django ==

  • 主路由匹配,分布式路由匹配
  • 通过路由匹配,获取路径中的‘用户名’, 转递给视图类中的get函数
  • 在处理函数中,根据用户名,进行查询,得到对应的用户数量, 结果>0则说明重复,否则不重复
  • 返回结果
    1.路由匹配
from django.urls import path 
from .views import *
urlpatterns = [
	path('check/username/<str:username>/', CheckUsername.as_view()),
]

2.视图接口

class CheckUsername(APIView):
	def get(self, request, username):
		"""
		1. 用户名格式不正确,返回 404
        2. 用户名格式正确,用户名重复,返回 400
        3. 用户名格式正确,也不重复,返回 200
		"""
		# 1.判断用户名格式
		# 要求字母开头, 中间为字母, 数字, 下划线, 长度5-16
		if not re.findall('^[a-zA-Z]\w{4,15}$, username')
			return Response({
				'code': 404,
				'msg': '格式不正确!'
				)}
		n = User.objects.filter(username=username).count()
		if n==0:
			return Response({
				'code':200, 'msg':'OK!'
			})
		else:
			return Response({
				'code':404, 'msg':'该用户已存在!'
			})

Vue前端用户名校验

  • 用户在文本框输入用户名
  • 使用v-model做数据的双向绑定
  • 输入完成之后,触发 失焦事件(blur), 执行 校验 用户名的 方法, 具体如下:
// 用户名的校验方法
let validateName = (rule, value, callback) => {
  if (!value) {
    return callback(new Error("请输入用户名"));
  }
  // 用户名以字母开头,长度在5-16之间,允许字母数字下划线
  const userNameRule = /^[a-zA-Z]\w{4,15}$/;
  if (userNameRule.test(value)) {
    // 前端校验,用户名复合规则
    //请求后端, 判断用户名是否重复
    this.$axios
      .get("/users/check/username/" + this.RegisterUser.name + "/"
      )
      .then((res) => {
        // 200代表用户名不重复,可以注册
        console.log("校验用户名是否重复:", res);
        if (res.data.code == 200) {
          this.$refs.ruleForm.validateField("checkPass");
          return callback();
        } else {
          // 用户名重复或者不符合规则
          return callback(new Error(res.data.msg));
        }
      })
      .catch((err) => {
        return Promise.reject(err);
      });
  } else {
    // 前端校验,用户名不符合规则
    return callback(new Error("字母开头,长度5-16之间,允许字母数字下划线"));
  }
};

密码的校验

// 密码的校验方法
let validatePass = (rule, value, callback) => {
  if (value === "") {
    return callback(new Error("请输入密码"));
  }
  // 密码以字母开头,长度在6-18之间,允许字母数字和下划线
  const passwordRule = /^[a-zA-Z]\w{5,17}$/;
  if (passwordRule.test(value)) {
    this.$refs.ruleForm.validateField("checkPass");
    return callback();
  } else {
    return callback(
      new Error("字母开头,长度6-18之间,允许字母数字和下划线")
    );
  }
};
// 确认密码的校验方法
let validateConfirmPass = (rule, value, callback) => {
  if (value === "") {
    return callback(new Error("请输入确认密码"));
  }
  // 校验是否以密码一致
  if (this.RegisterUser.pass != "" && value === this.RegisterUser.pass) {
    this.$refs.ruleForm.validateField("checkPass");
    return callback();
  } else {
    return callback(new Error("两次输入的密码不一致"));
  }
};

手机号重复性校验

API设计

# 手机号传递
GET  /users/check/mobile/xxxx/

django

  1. 路由匹配
from django.urls import path
from user.views import *

urlpatterns = [
    path('check/mobile/<str:mobile>/', CheckMobile.as_view()),
]

  1. 视图接口
class CheckMobile(APIView):
    def get(self, request, mobile):
        """
        1. 手机号格式不正确,返回 404
        2. 手机号重复,返回 400
        3. 手机号格式正确,也不重复,返回 200
        """
        # 1. 校验手机号格式,前后端都需校验
        # 手机号以1开头,第二位3-9之间的数字,长度为11,只允许数字
        if not re.findall(r"^1[3-9]\d{9}$", mobile):
            return Response({"code":404, 'msg':"手机号格式不正确"})
        # 2. 根据手机号查询用户数量
        n = User.objects.filter(mobile=mobile).count()
        # 3. 判断并返回结果
        if n == 0:
            return Response({"code":200, 'msg': 'OK'})
        else:
            return Response({"code":400, 'msg': '手机号重复'})

Vue前端手机号校验

  • 用户在文本框输入手机号
  • 通过 v-model 获取 手机号
  • 输入完成之后,触发 失焦事件(blur), 执行 校验 手机号的 方法,具体如下:
// 手机号的校验方法
let validateMobile = (rule, value, callback) => {
  if (value === "") {
    return callback(new Error("请输入手机号"));
  }
  // 手机号以1开头,第二位3-9之间的数字,长度为11,只允许数字
  const mobileRule = /^1[3-9]\d{9}$/;
  if (mobileRule.test(value)) {
    this.$axios.get("/users/check/mobile/" + this.RegisterUser.mobile + "/")
    .then(res=>{
      console.log("验证手机号是否可用:", res)
      if(res.data.code == 200){
        this.$refs.ruleForm.validateField("checkPass");
        return callback();
      }else{
        return callback(new Error(res.data.msg))
      }
    }).catch(err=>{
      return Promise.reject(err)
    })
  } else {
    return callback(new Error("手机号不符合格式"));
  }
};

图片验证码生成与展示

  1. django后端
    路由匹配
   from django.urls import path
   from user.views import *
   urlpatterns = [
    #/users/image_code/xxx/ 生成图片验证码
    path('image_code/<uuid:uuid>/', ImageCode.as_view()), #也可以<str:uuid>
   ]

         视图接口

   import random
   import string
   import redis
   from django.http.response import HttpResponse
   from rest_framework.views import APIView
   from rest_framework.response import Response
   from user.models import *
   from captcha.image import ImageCaptcha
class ImageCode(APIView):
    def get(self, request, uuid):
        # 1.接收参数uuid

        # 2. 生成随机字符串
        # string.ascii_letters: 所有字母的字符串(大小写)
        # string.digits: 所有的数字字符串(0-9)
        # random.sample: 随机采集指定位数的字符
        salt = ''.join(random.sample(string.ascii_letters + string.digits, 4))
        # 2. 生成 图片验证码 流
        img = ImageCaptcha()
        image = img.generate(salt) #io.BytesIO对象
        #from PIL import Image
        #im = Image.open(image)
        #im.show() #查看生成的图片验证码
    
        # 3. 连接redis
        redis_conn = redis.Redis(host='localhost', port=6379, db=0)
    
        # 4. 存储图片验证码,设置过期时间10分钟
        redis_conn.setex(str(uuid), 60 * 10, salt)
    
        # 5. 返回图片流
        return HttpResponse(image, content_type='image/png')

  1. Vue前端加载图片验证码
  • 定义方法genImageCode
  • 生成uuid
  • 拼接图片验证码的url
  • 将拼接好的图片验证码url绑定到 img 标签中
  • 默认的图片验证码,初始加载页面时,需要 在钩子函数中 执行方法
  • 主动生成图片验证码,定义图片的点击事件,触发方法,生成新的uuid,重新请求图片验证码
<!--  对img标签进行属性绑定          -->
<img :src="imageCodeUrl" alt="图形验证码" class="pic_code" @click="genImageCode">
<!-- 对img标签进行点击事件绑定 ,点击一次,执行一次方法,请求一次图片  -->


<script>
// 使用uuid模块,生成 uuid
import {v4 as uuid4} from 'uuid'   // 提前安装好uuid模块, npm install uuid --save 

export default {
    data() {
        return {
            //如下
            imageCodeID: '', // 实际就是uuid
            imageCodeUrl: '', // 图片验证码的地址
        }
    },
    methods: {
        //如下方法
        genImageCode() {
            //  1. 生成一个uuid
            this.imageCodeID = uuid4();

            // 2. 拼接一个图片地址
            this.imageCodeUrl = '/users/image_code/' + this.imageCodeID + '/'
        }
    },
    mounted() {
        //默认的图片验证码
        this.genImageCode();
    }
}
</script>

图片验证码认证

  1. django后端
    路由配置
#校验图片验证码
#/users/check_image_code/?imageCodeID=xxx&imageCode=xxxx
path('check_image_code/', CheckImageCode.as_view()),

         视图接口

class CheckImageCode(APIView):
    def get(self, request):
        """
        1. 接收前端发送的参数,imageCodeID & imageCode
        2. 取出redis中的 图片验证码
            如果取不到,说明过期,响应204
            如果取到验证码,进行对比,注意: 统一大小写
        3. 根据对比结果,返回响应
            对比成功,响应200
            对比失败,响应204
        """
        #1. 接收前端发送的参数,imageCodeID & imageCode
        uuid_ = request.query_params.get("imageCodeID")
        image_code_ = request.query_params.get("imageCode")

        # 2. 取出redis中的 图片验证码
        #     如果取不到,说明过期,响应204
        #     如果取到验证码,进行对比,注意: 统一大小写
        redis_conn = redis.Redis(host='localhost', port=6379, db=0)
        image_code = redis_conn.get(uuid_)

        if not image_code:
            return Response({"code":204, 'msg': '验证码过期'})

        # 3.
        # 根据对比结果,返回响应
        # 对比成功,响应200
        # 对比失败,响应204
        if image_code.decode().lower() == image_code_.lower():
            return Response({"code":200, 'msg': 'ok'})
        else:
            return Response({"code":204, 'msg': '验证码错误'})

  1. Vue前端校验输入的图片验证码
// 校验图片验证码
let validateImageCode = (rule, value, callback) => {
  if (value === "") {
    return callback(new Error("请输入图片验证码"));
  }
  // 图片验证码是由字母、数字组成,长度为4
  const iamgeCodeRule = /^[a-zA-Z0-9]{4}$/;
  if (iamgeCodeRule.test(value)) {
    this.$axios.get("/users/check_image_code/", {
      params:{
        imageCodeID: this.imageCodeID,
        imageCode: this.RegisterUser.imageCode,
      }
    })
    .then(res => {
      if(res.data.code == 200){
        this.$refs.ruleForm.validateField("checkPass");
        return callback();
      }else{
        return callback(new Error(res.data.msg))
      }
    }).catch(err => {
      return Promise.reject(err)
    })
  } else {
    return callback(new Error("图片验证码不正确!"));
  }
};

用户注册

  1. Vue前端接口
    点击注册,触发函数如下:
   // 用户注册
   Register() {
   // 是否同意用户协议
   if(!this.agree){
    this.flag = true
    return
   }
   // 已勾选,则不显示提示信息
   this.flag = false
   // 通过element自定义表单校验规则,校验用户输入的用户信息
   this.$refs["ruleForm"].validate((valid) => {
    //如果通过校验开始注册
    if (valid) {
      this.$axios
        .post("/users/register/", {
          userName: this.RegisterUser.name,
          pwd: this.RegisterUser.pass,
          mobile: this.RegisterUser.mobile,
          agree: this.aggree,
        })
        .then((res) => {
          // 200代表注册成功,其他的均为失败
          if (res.data.code == 200) {
            // 隐藏注册组件
            this.isRegister = false;
            // 弹出通知框提示注册成功信息
            this.notifySucceed(res.data.msg);
          } else {
            // 弹出通知框提示注册失败信息
            this.notifyError(res.data.msg);
          }
        })
        .catch((err) => {
          return Promise.reject(err);
        });
    } else {
      return false;
    }
   });
   },
  1. django后端
    路由匹配
# /users/register/
path('register/', Register.as_view()),

        视图接口

from rest_framework.views import APIView
from rest_framework.response import Response
from user.models import *

#注册
class Register(APIView):
    def post(self, request):
        # 前端传入 userName, pwd, mobile, agree
        username = request.data.get("userName")
        password = request.data.get("pwd")
        mobile = request.data.get("mobile")
        agree = request.data.get("agree")
        #验证参数
        if not all([username, password, mobile]):
            return Response({"code":204, "msg":"注册信息不完整!"})
        if not agree:
            return Response({"code":204, 'msg':"未同意用户使用协议"})

        # 存入数据库
        User.objects.create_user(username=username, password=password, mobile=mobile)

        # 返回响应
        return Response({"code":200, "msg":"注册成功!"})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值