第一步: 前期分析
可见, 前端会 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 上, 见下图
--- 君子处其实, 不处其华; 治其内, 不治其外 张居正 ----
来源: https://www.cnblogs.com/jiangzongyou/p/12112079.html