当前位置: 首页 > news >正文

第11次:用户注册(完整版)

第一步:定义用户模型类

class User(AbstractUser):mobile = models.CharField(max_length=11, unique=True, verbose_name='手机号')class Meta:db_table = 'tb_user'verbose_name = '用户'verbose_name_plural = verbose_namedef __str__(self):return self.username

第二步:在配置文件中配置自定义User模型类,用自定义的User模型类代替Django自带的User模型类

#dev.py
# 指定本项目用户模型类,让Django自带的User类不起作用
AUTH_USER_MODEL = 'users.User'

第三步:生成数据表

python .\manage.py makemigrations
python .\manage.py migrate

第四步:定义视图类,处理注册时请求

import refrom django.db import DatabaseError
from django.http import HttpResponseForbidden
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View# from xiaoyu_mall.apps.users.models import User    应用目录变更后不要使用这种方式来导包
# 推荐使用如下的相对路径来导包
from .models import Userclass RegisterView(View):# get请求,def get(self, request):return render(request, 'register.html')def post(self, request):# 第一步:接收请求参数username = request.POST.get('username')password = request.POST.get('password')password2 = request.POST.get('password2')mobile = request.POST.get('mobile')allow = request.POST.get('allow')# 第二步:判断参数是否完整if not all([username, password, mobile, allow]):return HttpResponseForbidden('缺少必要参数')# 用户名长度校验if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):return HttpResponseForbidden('请输入5-20个字符的用户名')# 检验密码格式if not re.match(r'^[0-9A-Za-z]{8,20}$', password):return HttpResponseForbidden('请输入8-20位的密码')# 检验两次密码一致if password != password2:return HttpResponseForbidden('两次密码不一致')# 校验手机号if not re.match(r'^1[3-9]{9}$', mobile):return HttpResponseForbidden('请输入正确的手机号')# 提醒勾选协议if allow != 'on':return HttpResponseForbidden('请勾选协议')# 第三步:保存注册数据try:User.objects.create_user(username=username, password=password, mobile=mobile)except DatabaseError:return render(request, 'register.html', {'register_errmsg': '注册失败'})# 返回注册结果return redirect(reverse('contents:index'))

第五步:定义根路由和子路由

#根路由
from django.contrib import admin
from django.urls import path, includeurlpatterns = [path('admin/', admin.site.urls),# 导入path('', include('users.urls', namespace='users')),
]
from django.urls import path
# 变更了应用所在目录之后,不要使用这种方式导包
# from xiaoyu_mall.apps.users import views
# 推荐使用如下的相对路径来导包
from . import views
# 设置命名空间
app_name = 'users'
urlpatterns = [path('register/', views.RegisterView.as_view(), name='register'),
]

第六步:注册过程中的前端校验与后端校验

  • 前端校验使用vue-2.5.16.js实现,预先校验表单数据的合法性,定义在register.js中
  1. 用户名与手机号先要经过前端正则校验后,再向后端发送查询请求,确定数据库中没有重复数据之后再保存。
  2. 密码要在先在前端正则检验
  3. 要校验确认密码与密码是否一致
  4. 要校验手机号的格式
  5. 要校验是否勾选协议

在static/js/目录下创建register.js

let vm = new Vue({el: '#app',// 修改Vue读取变量的语法delimiters: ['[[', ']]'],data: {username: '',		// 用户名password: '', 		// 密码password2: '',		// 确认密码mobile: '',			// 手机号allow: '',			// 同意协议uuid: '',image_code_url: '',	// 图形验证码image_code: '',error_name: false,error_password: false,error_password2: false,error_mobile: false,error_allow: false,error_image_code: false,error_name_message: '',		// 用户名错误提示error_mobile_message: '',	// 手机错误提示error_image_code_message: '',},mounted(){// 界面获取图形验证码this.generate_image_code();},methods: {// 生成图形验证码generate_image_code(){// 生成UUID。generateUUID() : 封装在common.js文件中,需要提前引入this.uuid = generateUUID();// 拼接图形验证码请求地址this.image_code_url = "/image_codes/" + this.uuid + "/";},// 校验用户名check_username(){// 准备正则表达式let re = /^[a-zA-Z0-9_-]{5,20}$/;// 正则表达式匹配用户名if (re.test(this.username)) {this.error_name = false;} else {this.error_name_message = '请输入5-20个字符的用户名';this.error_name = true;}// 检查用户名是否重名注册if (this.error_name == false) {let url = '/usernames/' + this.username + '/count/';axios.get(url,{responseType: 'json'}).then(response => {if (response.data.count == 1) {this.error_name_message = '用户名已存在';this.error_name = true;} else {this.error_name = false;}}).catch(error => {console.log(error.response);})}},// 校验密码check_password(){let re = /^[0-9A-Za-z]{8,20}$/;if (re.test(this.password)) {this.error_password = false;} else {this.error_password = true;}},// 校验确认密码check_password2(){// 判断两次密码是否一致if(this.password != this.password2) {this.error_password2 = true;} else {this.error_password2 = false;}},// 校验手机号check_mobile(){let re = /^1[3-9]\d{9}$/;if(re.test(this.mobile)) {this.error_mobile = false;} else {this.error_mobile_message = '您输入的手机号格式不正确';this.error_mobile = true;}// 检查手机号是否重复注册if (this.error_mobile == false) {let url = '/mobiles/'+ this.mobile + '/count/';axios.get(url, {responseType: 'json'}).then(response => {if (response.data.count == 1) {this.error_mobile_message = '手机号已存在';this.error_mobile = true;} else {this.error_mobile = false;}}).catch(error => {console.log(error.response);})}},// 检查图形验证码check_image_code(){if(this.image_code.length != 4) {this.error_image_code_message = '请填写图片验证码';this.error_image_code = true;} else {this.error_image_code = false;}},// 校验是否勾选协议check_allow(){if(!this.allow) {this.error_allow = true;} else {this.error_allow = false;}},// 监听表单提交事件on_submit(){this.check_username();this.check_password();this.check_password2();this.check_mobile();this.check_allow();if(this.error_name == true || this.error_password == true || this.error_password2 == true|| this.error_mobile == true || this.error_allow == true) {// 禁用表单的提交window.event.returnValue = false;}},}
});

common.js

// 获取cookie
function getCookie(name) {let r = document.cookie.match("\\b" + name + "=([^;]*)\\b");return r ? r[1] : undefined;
}// 提取地址栏中的查询字符串
function get_query_string(name) {let reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');let r = window.location.search.substr(1).match(reg);if (r != null) {return decodeURI(r[2]);}return null;
}// 生成uuid
function generateUUID() {let d = new Date().getTime();if(window.performance && typeof window.performance.now === "function"){d += performance.now(); //use high-precision timer if available}let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {let r = (d + Math.random()*16)%16 | 0;d = Math.floor(d/16);return (c=='x' ? r : (r&0x3|0x8)).toString(16);});return uuid;
}
  • 后端校验通过视图来实现,通过查询数据库校验用户名、手机号在系统中的是否唯一。

users应用下views.py

from xiaoyu_mall.utils.response_code import RETCODEclass MobileCountView(View):"""手机号唯一性校验"""def get(self, request, mobile):count = User.objects.filter(mobile=mobile).count()return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})class UsernameCountView(View):"""判断用户名是否重复注册"""def get(self, request, username):count = User.objects.filter(username=username).count()return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})

上面代码用的状态码定义在xiaoyu_mall/xiaoyu_mall/utils/response_code.py中

class RETCODE:OK                  = "0"IMAGECODEERR        = "4001"THROTTLINGERR       = "4002"NECESSARYPARAMERR   = "4003"USERERR             = "4004"PWDERR              = "4005"CPWDERR             = "4006"MOBILEERR           = "4007"SMSCODERR           = "4008"ALLOWERR            = "4009"SESSIONERR          = "4101"DBERR               = "5000"EMAILERR            = "5001"TELERR              = "5002"NODATAERR           = "5003"NEWPWDERR           = "5004"OPENIDERR           = "5005"PARAMERR            = "5006"STOCKERR            = "5007"err_msg = {RETCODE.OK                 : "成功",RETCODE.IMAGECODEERR       : "图形验证码错误",RETCODE.THROTTLINGERR      : "访问过于频繁",RETCODE.NECESSARYPARAMERR  : "缺少必传参数",RETCODE.USERERR            : "用户名错误",RETCODE.PWDERR             : "密码错误",RETCODE.CPWDERR            : "密码不一致",RETCODE.MOBILEERR          : "手机号错误",RETCODE.SMSCODERR          : "短信验证码有误",RETCODE.ALLOWERR           : "未勾选协议",RETCODE.SESSIONERR         : "用户未登录",RETCODE.DBERR              : "数据错误",RETCODE.EMAILERR           : "邮箱错误",RETCODE.TELERR             : "固定电话错误",RETCODE.NODATAERR          : "无数据",RETCODE.NEWPWDERR          : "新密码数据",RETCODE.OPENIDERR          : "无效的openid",RETCODE.PARAMERR           : "参数错误",RETCODE.STOCKERR           : "库存不足",
}
  • 路由配置,在users应用下urls.py中,
from django.urls import path, re_path
# 变更了应用所在目录之后,不要使用这种方式导包
# from xiaoyu_mall.apps.users import views
# 推荐使用如下的相对路径来导包
from . import views# 设置命名空间
app_name = 'users'
urlpatterns = [path('register/', views.RegisterView.as_view(), name='register'),re_path('usernames/(?P<username>[a-zA-Z0-9_-]{5,20})/count/', views.UsernameCountView.as_view()),re_path(r'mobiles/(?P<mobile>1[3-9]\d{9})/count/', views.MobileCountView.as_view()),
]

第七步:图形验证

安装依赖库

pip install pillow
pip install captcha

图形验证码要存在redis数据库中,在xiaoyu_mall/dev.py配置使用redis的2号库存图形验证码

CACHES = {"default": {  # 默认"BACKEND": "django_redis.cache.RedisCache","LOCATION": "redis://127.0.0.1:6379/0","OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient",}},"session": {  # session"BACKEND": "django_redis.cache.RedisCache","LOCATION": "redis://127.0.0.1:6379/1","OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient",}},"verify_code": {  # 保存验证码"BACKEND": "django_redis.cache.RedisCache","LOCATION": "redis://127.0.0.1:6379/2",  # 选择redis2号库"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient",}},
}

在verifications应用中创建constants.py,用于设置图形验证码有效期的变量

# 图形验证码有效期,单位:秒
IMAGE_CODE_REDIS_EXPIRES = 300
# 短信验证码有效期,单位:秒
SMS_CODE_REDIS_EXPIRES = 300

在verifications应用中创建verify_pic.py

from captcha.image import ImageCaptcha
import io
import random
import string# 生成随机验证码
def generate_captcha_text():captcha_text = ''.join(random.choices(string.ascii_letters +string.digits, k=4))return captcha_text# 生成验证码图片
def generate_captcha_image(text):image = ImageCaptcha()data = image.generate_image(text)img_byte_array = io.BytesIO()data.save(img_byte_array, format='PNG')binary_image = img_byte_array.getvalue()return binary_image

在verifications应用下views.py定义生成图形验证码的类视图

from django.views import View
from .verify_pic import generate_captcha_text,generate_captcha_image
from django_redis import get_redis_connection
from . import constants
from django.http import HttpResponse
import logging# 日志记录器
logger = logging.getLogger('django')class ImageCodeView(View):def get(self, request, uuid):text = generate_captcha_text()image = generate_captcha_image(text)# print(text)  # 输出生成的验证码redis_conn = get_redis_connection('verify_code')  # 保存图形验证码# setex 保存到redis中 并设置生存时间redis_conn.setex('img_%s' % uuid,constants.IMAGE_CODE_REDIS_EXPIRES, text)# 响应图形验证码return HttpResponse(image, content_type='image/jpg')

配置根路由

from django.contrib import admin
from django.urls import path, includefrom xiaoyu_mall.apps import verificationsurlpatterns = [path('admin/', admin.site.urls),# 导入path('', include('users.urls', namespace='users')),path('', include('contents.urls', namespace='contents')),path('',include('verifications.urls')),
]

配置verifications应用下子路由

from django.urls import path, re_path
# 变更了应用所在目录之后,不要使用这种方式导包
# from xiaoyu_mall.apps.users import views
# 推荐使用如下的相对路径来导包
from . import viewsurlpatterns = [path('image_codes/<uuid:uuid>', views.ImageCodeView.as_view(), name='register'),
]

相关文章:

  • OpenHarmony GPIO应用开发-LED
  • 63.微服务保姆教程 (六) SkyWalking--分布式链路追踪系统/分布式的应用性能管理工具
  • 原生 IP(Native IP)
  • C++23 std::generator:用于范围的同步协程生成器 (P2502R2, P2787R0)
  • FlinkCDC采集MySQL8.4报错
  • 如何监控Kafka的Lag(消费延迟)?
  • RT-Thread中的配置
  • MySQL同步ES的6种方案!
  • [三分钟]性能测试工具JMeter入门: 下载安装JMeter并设置中文;JMeter基本使用流程
  • 解锁科研文献检索密码:多工具协同攻略
  • 给frp设置开机自启
  • 应急响应靶场web1:知攻善防实验室
  • 分布式 ID 的技术解析与实现实践
  • 【Java EE初阶 --- 多线程(初阶)】多线程的基本内容
  • ZYNQ-UART串口中断
  • 【Java篇】内存中的桥梁:Java数组与引用的灵动操作
  • 前端封装框架依赖管理全攻略:构建轻量可维护的私有框架
  • livp文件使用python转换为heic或jpeg格式
  • k8s node cgroup 泄露如何优化?
  • 深入理解 Java 观察者模式:原理、实现与应用
  • 上海国际电影节特设“走进大卫·林奇的梦境”单元
  • 实探北京楼市:“好房子”卖点十足,二手房持续回稳
  • 长线游、县域游、主题游等持续升温,假期文旅市场供需两旺
  • 深入景区、文化街区及消费一线,多地省委书记调研文旅市场
  • 医生李某某饮酒上班?重庆长寿区人民医院:正在调查,将严肃处理
  • 人民日报头版:让青春之花绽放在祖国和人民最需要的地方