发送验证码的过程可以看做是对model设计时的VerifyCode表的操作,是一个created的操作,为了提供这个接口,我们要写相应的ViewSet,在users下的views.py下,我们引入:
from rest_framework.mixins import CreateModelMixin
这个类中有create方法可以重载。
当然还要有:
from rest_framework import viewsets
我们新建的类将继承它们。
即:
class SmsCodeViewSet(CreateModelMixin, viewsets.GenericViewSet):
"""
发送短信验证码
"""
pass
要开始写逻辑了,在发送短信(创建验证码记录)之前,我们首先要做三个验证:一是用户输入的是否是合法的手机号,二是手机号是否被注册过,三是同一手机号码的请求频率。我们之前说过,DRF的Serializer和Django中的ModelForm很像,所以验证相关的逻辑我们就放在Serializer中做。在users下新建一个serializers.py,编辑:
from rest_framework import serializers
from django.contrib.auth import get_user_model
import re
from datetime import datetime
from datetime import timedelta
from MxShop.settings import REGEX_MOBILE
from .models import VerifyCode
User = get_user_model()
class SmsSerializer(serializers.Serializer): # 这里不继承ModelSerializer,因为在model设计时,code是必填字段
# 如果用了ModelSerializer,就会将表单与VerifyCode做一个关联,则在点击发送验证码时,会报验证码必填的错
# 所以继承Serializer自己写逻辑进行保存
mobile = serializers.CharField(max_length=11) # 有了这个声明,下面就可以针对这个mobile做一些检验了
def validate_mobile(self, mobile):
"""
验证手机号码
"""
# 手机是否注册
if User.objects.filter(mobile=mobile).count():
raise serializers.ValidationError("用户已经存在")
# 验证手机号码是否合法
if not re.match(REGEX_MOBILE, mobile):
raise serializers.ValidationError("手机号码非法")
# 验证码发送频率
one_mintes_ago = datetime.now() - timedelta(hours=0, minutes=1, seconds=0)
if VerifyCode.objects.filter(add_time__gt=one_mintes_ago, mobile=mobile).count():
raise serializers.ValidationError("距离上一次发送未超过60s")
return mobile
并在settings.py中新增:
#手机号码正则表达式
REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|^176\d{8}$"
有了这个serializer,我们就可以完成SmsCodeViewSet了。在users/views.py中,引入刚刚的serializer:
from .serializers import SmsSerializer
然后在SmsCodeViewSet中添加一句:
serializer_class = SmsSerializer
这样以后,我们完成了手机号的验证,就可以开始进行create操作了。点进我们继承的CreateModelMixin中,我们重载它的create方法,拷贝它到SmsCodeViewSet中,并修改为(大家可以看看下面的与原来的有什么不同):
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data) # 拿到上边的serializer
serializer.is_valid(raise_exception=True) # 这里如果=True,这一步如果出错,直接抛异常,不会进入下面的代码行;这里is_valid出错,返回400
mobile = serializer.validated_data["mobile"] # 上面已经验证了,能跑到这一步,所以mobile一定是有的
yun_pian = YunPian(APIKEY)
code = self.generate_code()
sms_status = yun_pian.send_sms(code=code, mobile=mobile) # 返回的是re_dict,可以点进去看看
# 下面来解析这个变量
if sms_status["code"] != 0: # 返回的status代表的含义可以看一看云片网的文档
return Response({
"mobile":sms_status["msg"] # msg中放的是云片网错误信息
}, status=status.HTTP_400_BAD_REQUEST)
else:
code_record = VerifyCode(code=code, mobile=mobile)
code_record.save()
return Response({
"mobile":mobile
}, status=status.HTTP_201_CREATED) # 成功后返回的是手机号
引入引用的外部文件:
from rest_framework.response import Response
from rest_framework import status
from utils.yunpian import YunPian
from MxShop.settings import APIKEY
from .models import VerifyCode
为此我们还要写一个生成验证码的方法,依旧是在SmsCodeViewSet中,添加一个方法:
def generate_code(self):
"""
生成四位数字的验证码
"""
seeds = "1234567890" # 种子
random_str = []
for i in range(4):
random_str.append(choice(seeds))
return "".join(random_str) # 原本是一个数组
引入:
from random import choice
我们现在可以来调试一下,在create方法中打断点。然后要给它注册一个url,在根urls中,添加:
router.register(r'code', SmsCodeViewSet, base_name="code")
和
from users.views import SmsCodeViewSet
这个时候访问codes页面,发现GET方法不被允许。
但底下又出现了一个post框,我们可以输入mobile然后POST(之前的login页面也可以,相当于postman了)。可以先输入错误格式的手机号,看看错误提示:
首先是是http status,然后错误信息是以键值对的形式展示的,键为出错字段的名称,值为数组,列出了所有的错误。