一、功能描述:
用户提交手机号,获取验证码,输入密码后,注册并登录。
二、发送短信验证码
1、首先要校验手机号是否合法,是否已经注册,还要限制发送验证码不能过于频繁。
# 发送短信序列化
class SmsSerializer(serializers.Serializer):
mobile = serializers.CharField(max_length=11)
def validate_mobile(self, mobile): # validate_字段 验证
"""
验证手机号码
:param data:
:return:
"""
# 手机是否注册
if User.objects.filter(mobile=mobile).count():
raise serializers.ValidationError("用户已经存在")
# 验证手机号码是否合法
if not re.match(REGEX_MOBILE, mobile):
raise serializers.ValidationError("手机号码非法")
# 验证码发送频率,60秒发一次
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
定义一个工具类,向验证码服务商发送请求:
import json
import requests
class YunPian(object):
def __init__(self, api_key):
self.api_key = api_key
self.single_send_url = "https://sms.yunpian.com/v2/sms/single_send.json"
def send_sms(self, code, mobile):
"""
:param code: 验证码
:param mobile: 手机号
:return:
"""
params = {
"apikey": self.api_key,
"mobile": mobile,
"text": "您的验证码是{code}.".format(code=code)
}
response = requests.post(self.single_send_url, data=params)
re_dict = json.loads(response)
return re_dict
视图类,发送验证码的功能:
class SmsCodeViewset(CreateModelMixin, viewsets.GenericViewSet):
# 发送短信验证码
serializer_class = SmsSerializer
# 要发送验证码,生成四位验证码
def get_code(self):
seeds = '1234567890' # 随机种子
random_str = []
for i in range(4):
random_str.append(choice(seeds))
return "".join(random_str)
# 重写create方法,向短信服务网站发起请求
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) # 出现错误,直接抛出异常,不会向下执行
mobile = serializer.validated_data['mobile'] # validated_data 获取数据
# 发短信
yun_pian = YunPian(APIKEY)
code = self.get_code()
sms_status = yun_pian.send_sms(code=code, mobile=mobile) # sms_status得到返回的状态
if sms_status["code"] != 0:
return Response({
"mobile": sms_status["msg"]
}, status=status.HTTP_400_BAD_REQUEST)
else:
# 保存验证码
code_record = VerifyCode(code=code, mobile=mobile)
code_record.save()
return Response({
"mobile": sms_status["msg"]
}, status=status.HTTP_201_CREATED) # 成功
三、注册
1、序列化类
class UserRegSerializer(serializers.ModelSerializer):
code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4, label='验证码',
error_messages={
'blank': '请输入验证码',
'required': '请输入验证码',
'max_length': '验证码格式错误',
'min_length': '验证码格式错误'
}, help_text='验证码')
username = serializers.CharField(label='用户名', help_text='用户名', required=True, allow_blank=False,
validators=[UniqueValidator(queryset=User.objects.all(), message='用户已存在')])
password = serializers.CharField(
style={'input_type': 'password'}, help_text='密码', label='密码', write_only=True,
) # write_only 数据不返回来
def validate_code(self, code):
"""
对验证码就行检验
:param code:
:return:
"""
verify_records = VerifyCode.objects.filter(mobile=self.initial_data['username']).order_by('-add_time')
# initial_data 用户传过来的值都在这里面
if verify_records:
# 如果用户多次发送,取出最新验证码
last_record = verify_records[0]
five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
if five_mintes_ago > last_record.add_time:
raise serializers.ValidationError('验证码过期')
if last_record.code != code:
raise serializers.ValidationError('验证码错误')
else:
raise serializers.ValidationError('验证码错误')
def validate(self, attrs): #全局校验
attrs['mobile'] = attrs['username']
del attrs['code'] # 不会保存到数据库中,多余字段,到这已经通过code校验,可以删除
return attrs
class Meta:
model = User
fields = ('username', 'code', 'mobile', 'password')
2、视图类
class UserViewset(CreateModelMixin, viewsets.GenericViewSet):
"""
用户注册
"""
serializer_class = UserRegSerializer
queryset = User.objects.all()
def create(self, request, *args, **kwargs):
#重写create方法
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = self.perform_create(serializer) # 保存
re_dict = serializer.data
payload = jwt_payload_handler(user)
re_dict["token"] = jwt_encode_handler(payload) # 获取token
re_dict["name"] = user.name if user.name else user.username
#返回浏览器保存,不保存在服务器
headers = self.get_success_headers(serializer.data)
return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)
def get_object(self):
return self.request.user
def perform_create(self, serializer):
return serializer.save()
3、Django信号
在保存用户信息时,密码会保存成明文,所以引入了Django信号量机制。
from django.dispatch import receiver
from django.contrib.auth import get_user_model
User = get_user_model()
@receiver(post_save, sender=User) # 用户提交的数据即将保存时,对密码加密
def create_user(sender, instance=None, created=False, **kwargs):
if created:
password = instance.password
instance.set_password(password)
instance.save()