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('服务器超时,请重试!')
}
})
});
});
页面效果:
数据库中: