VIIII. 注册功能
谋定而后动, 先做分析在写代码
1>业务流程分析
- 对参数进行校验
- 判断用户名是否为空,是否已注册
- 判断密码是否为空, 是否一致,格式是否正确
- 判断手机号码是否为空,格式是否正确
- 判断短信验证码是否为空,格式是否正确,是否与真实短信验证码相同
- 新建数据库记录
2>接口设计
2.1>接口说明:
类目 | 说明 |
---|---|
请求方法 | POST |
url定义 | /user/register/ |
参数格式 | 表单 |
注意:post请求,前端请求要带上csrf token
2.2>参数说明:
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
username | 字符串 | 是 | 用户输入的用户名 |
password | 字符串 | 是 | 用户输入的密码 |
password_repeat | 字符串 | 是 | 用户输入的重复密码 |
mobile | 字符串 | 是 | 用户输入的手机号码 |
sms_code | 字符串 | 是 | 用户输入的短信验证码 |
2.3>返回结果:
{
"errno": "0",
"errmsg": "恭喜您,注册成功!",
}
我们选择使用ajax来实现注册提交
3.后端代码
-
user/views.py代码:
from django.shortcuts import render from django.views import View from .forms import RegisterForm from .models import User from utils.json_res import json_response from utils.res_code import Code, error_map class RegisterView(View): """ 注册视图 /users/register """ def get(self, request): """ 凡是来访问这个视图的请求, 就返回注册页面 :param request: 请求注册页面 :return: 注册页面 """ return render(request, 'users/register.html') def post(self, request): # 1>校验数据 form = RegisterForm(request.POST) if form.is_valid(): # 2>创建数据 username = form.cleaned_data.get('username') password = form.cleaned_data.get('password') mobile = form.cleaned_data.get('mobile') User.objects.create_user(username=username, password=password, mobile=mobile) return json_response(errmsg='恭喜你,注册成功!') else: # 引发的错误可能会有多条错误信息,如果出错了,就将表单的报错信息进行拼接, err_msg_list = [] for item in form.errors.values(): # 遍历的item也是一个列表, 其中的第一个元素是报错信息 err_msg_list.append(item[0]) err_msg_str = '/'.join(err_msg_list) # 由于是参数错误, 所以使用Code.PARAMERR return json_response(errno=Code.PARAMERR, errmsg=err_msg_str)
-
user/forms.py代码:
import re from django import forms from django_redis import get_redis_connection from .models import User from verification.constants import SMS_CODE_LENGTH class RegisterForm(forms.Form): username = forms.CharField(label='用户名', max_length=20, min_length=5, error_messages={ 'max_length': '用户名长度要小于20', 'min_length': '用户名长度要大于4', 'required': '用户名不能为空' }) password = forms.CharField(label='密码', max_length=20, min_length=6, error_messages={ 'max_length': '密码长度要小于20', 'min_length': '密码长度要大于5', 'required': '用户名不能为空' }) password_repeat = forms.CharField(label='确认密码', max_length=20, min_length=6, error_messages={ 'max_length': '密码长度要小于20', 'min_length': '密码长度要大于5', 'required': '用户名不能为空' }) mobile = forms.CharField(label='手机号码', max_length=11, min_length=11, error_messages={ 'max_length': '手机号码长度有误', 'min_length': '手机号码长度有误', 'required': '手机号码不能为空' }) sms_code = forms.CharField(label='短信验证码', max_length=SMS_CODE_LENGTH, min_length=SMS_CODE_LENGTH, error_messages={ 'max_length': '短信验证码长度有误', 'min_length': '短信验证码长度有误长度有误', 'required': '短信验证码不能为空' }) # 当想要详细的提示用户的错误时,建议使用这种单字段校验 def clean_username(self): """ 校验用户名 :return: """ username = self.cleaned_data.get('username') if User.objects.filter(username=username).exists(): return forms.ValidationError('用户名已存在!') return username def clean_mobile(self): """ 校验手机号 :return: """ mobile = self.cleaned_data.get('mobile') if not re.match(r'^1[3-9]\d{9}$', mobile): raise forms.ValidationError('手机号码格式不正确') if User.objects.filter(mobile=mobile).exists(): raise forms.ValidationError('手机号码已注册!') return mobile def clean(self): """ 联合校验,密码,和短信验证码 :return: """ clean_data = super().clean() # 校验密码是否一致 password = clean_data.get('password') password_repeat = clean_data.get('password_repeat') if password != password_repeat: raise forms.ValidationError('两次密码不一致!') # 校验短信验证码 sms_code = clean_data.get('sms_code') moblie = clean_data.get('mobile') redis_conn = get_redis_connection(alias='verify_code') real_code = redis_conn.get('sms_text_{}'.format(moblie)) if (not real_code) or (real_code.decode('utf-8') != sms_code): raise forms.ValidationError('短信验证码错误!') return clean_data
4.前端js代码
$(function () {
// 定义状态变量
let isUsernameReady = false,
isPasswordReady = false,
isMobileReady = false;
// 1.点击刷新图像验证码
let $img = $('.form-contain .form-item .captcha-graph-img img');
$img.click(function () {...});
// 2.鼠标离开用户名输入框校验用户名
let $username = $('#username');
$username.blur(fnCheckUsername);
function fnCheckUsername () {....}
// 3.检测密码是否一致
let $passwordRepeat = $('input[name="password_repeat"]');
$passwordRepeat.blur(fnCheckPassword);
function fnCheckPassword () {...}
// 4.检查手机号码是否可用
let $mobile = $('input[name="mobile"]');
$mobile.blur(fnCheckMobile);
function fnCheckMobile () {...}
// 5.发送手机验证码
let $smsButton = $('.sms-captcha');
$smsButton.click(function () {...});
// 6.注册
let $submitBtn = $('.register-btn');
$submitBtn.click((e)=>{
// 阻止默认提交, 目的是防止点击立即注册后提交数据
e.preventDefault();
// 点击后的工作,首先是校验我们的数据,可以直接复用上面的定义方法
// 1.校验用户名
if(!isUsernameReady){
fnCheckUsername();
return
}
// 2.校验密码
if(!isPasswordReady){
fnCheckPassword();
return
}
// 3.校验电话号码
if(!isMobileReady){
fnCheckMobile();
return
}
// 4.校验短信验证码
let sSmsCode = $('input[name="sms_captcha"]').val();
if(sSmsCode === ''){
message.showError('短信验证码不能为空!');
return
}
if(!(/^\d{4}$/).test(sSmsCode)){
message.showError('短信验证码长度不正确,必须是4位数字!');
return
}
$.ajax({
url: '/users/register/',
type: 'POST',
data:{
username: $username.val(),
password: $('input[name="password"]').val(),
password_repeat: $passwordRepeat.val(),
mobile: $mobile.val(),
sms_code: sSmsCode,
},
dataType: 'json',
success: function (res) {
if(res.errno === '0'){
message.showSuccess('恭喜您,注册成功!');
// 注册成功后延时1.5秒跳转
setTimeout(function () {
//注册成功后重定向到登录页面
window.location.href = '/users/login/'
}, 1500)
}else{
//注册失败
message.showError(res.errmsg)
}
},
error: function () {
message.showError('服务器超时,请重试!')
}
})
});
});
页面效果:
数据库中: