第一步: 前期分析
可见, 前端会POST过来三个字段, 所以这里我们应该使用 mixins.CreateModelMixin
由于我们自定义的Userprofile中只有name是必填字段(注意:django自带的user中username是必填字段), 所以我们可以在serializers.py中使用 serializers.ModelSerializer
code的验证需要单独的validate_code()方法
- 并没有向手机号发送验证码, 用户随便输入的
- 有发送过验证码, 但是超过了5分钟过期限制
- 有发送过验证码, 但是用户输错了
- 如果发送了多条验证码, 应该只核对用户收到的最新的那个验证码
但是有一个问题, 传过来的code是我们Userprofile中没有的字段, 怎么使用ModelSerializer时将code验证并过滤掉? 答:先增加一个code字段, 然后使用全局钩子删掉code这个字段
注意: 上面这个页面, Vue前端post过来的手机号码放在username属性里的, 所以我们在取手机号码的时候, 应该用来self.initial_data["username"]取, 然后我们自己将其复制到mobile属性中去
这里我们还需要学会自定义错误提示信息 error_messages,
虽然在发送验证码阶段, 已经验证了手机号码是否存在,但在这里由于前端post的是用户名,所以也要对用户名进行去重验证 UniqueValidator,校验是否已经存在
第二步: 写代码
1.首先写UserSerializer, 专门来处理code的验证, 和Username去重的验证
from rest_framework import serializers from django.contrib.auth import get_user_model from rest_framework.validators import UniqueValidator # 给username字段做查重用 User = get_user_model() # 可以获取数据库的userprofile表 # 验证码校验 class UserRegSerializer(serializers.ModelSerializer): # 由于userprofile并没有code字段,所以先加进来 code = serializers.CharField(required=True, max_length=4, min_length=4, # 自定义的错误提示信息 error_messages={ "blank":"你的验证码哪儿去了", # blank针对的是有字段名,但没有字段值 "required":"请输入验证码", # required针对的是连字段名都没有 "max_length":"验证码长度错误", "min_length":"验证码长度错误", }, help_text="验证码") # 虽然在发送验证码阶段, 已经验证了手机号码是否存在,但在这里由于前端post的是用户名,所以也要对用户名进行验证,校验是否已经存在 username = serializers.CharField(required=True,allow_blank=False, validators=[UniqueValidator(queryset=User.objects.all(),message="用户已经存在")]) # 局部钩子, 只对code进行校验 def validate_code(self, code): # POST过来的数据封装在self.initial_data里, 且传递过来手机号的key是username # 一定要按时间排序,因为我们只会取最后一条code来验证 verify_records = VerifyCode.objects.filter(mobile=self.initial_data["username"]).order_by("add_time") # 如果验证码存在 if verify_records: # 取最新的那一条验证码 last_records = verify_records[0] # 验证码有效期为5min five_minute_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0) if five_minute_ago < last_records.add_time: raise serializers.ValidationError("验证码过期") # 验证码核对 if last_records.code != code: raise serializers.ValidationError("验证码错误") # 如果验证码不存在, 则直接报错 else: raise serializers.ValidationError("验证码错误") # 全局钩子, 目的是过滤掉code字段(code其实只用于验证,而不用存在userprofile表中) # attrs是局部钩子清洗后的所有字段的dict def validate(self, attrs): attrs["mobile"] = attrs["username"] del attrs["code"] return attrs class Meta: model = User fields = ("username", "code", "mobile")
然后是views.UserViewset
# 用户注册逻辑 class UserViewset(CreateModelMixin,viewsets.GenericViewSet): serializer_class=UserRegSerializer
别忘记配置url
# 用户注册接口(包含code验证,username去重验证) router.register(r'users', UserViewset, basename="users")
2. 最后去浏览器验证
第三步: 'UserProfile' object has no attribute 'code' 异常处理
1. 现在我们人为往数据库中添加一个手机号和验证码(省钱), 然后尝试着去POST, 最后的结果就是提示userprofile表没有code字段, 前面我们明明已经做的那么好了, 甚至还刻意删掉了code字段, 为什么还是报错???
2. 主要问题处在REST自带的 CreateModelMixin 上, 见下图
--- 君子处其实,不处其华;治其内,不治其外 张居正 ----