上两节分别完成了短信注册的功能和接口,本节课完成提交注册表单的功能,为注册功能提供接口。
回顾一下注册页面,有三个输入框(手机号、短信验证码、密码)和两个按钮(获取验证码、注册并登录)。
我们这次完成的接口就是用户在点击注册并登陆按钮后所调用的接口。
在users/views.py中编写一个UserViewSet类,这个类完成POST一个user到数据库中的任务。在完成它之前,我们需要先完成serializer的编写,完成验证码正确性的认证、用户是否存在的认证,并对数据进行了序列化,在serializers.py中:
from rest_framework.validators import UniqueValidator
class UserRegSerializer(serializers.ModelSerializer):
# 这里继承了ModelSerializer,虽然code不是user的字段,但仍然可以
# 一些技巧可以让我们既享受到ModelSerializer给我们带来的好处(不需要自己序列化),又突破它的一些限制
# 和上面不同,这里是多了一个code字段,而上面是少,所以这里好处理,删了就行
code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4,label="验证码",
error_messages={ # write_only=True,序列化时不会有这个字段,不这样会报错
"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="用户已经存在")])
# 参见DRF官方文档查看Validator的使用,验证username是否唯一
password = serializers.CharField(
style={'input_type': 'password'},help_text="密码", label="密码", write_only=True,
) # write_only=True后,这样password就不会返回了;style={'input_type': 'password'}后,DRF接口调试也是密文了
def validate_code(self, code):
# try:
# verify_records = VerifyCode.objects.get(mobile=self.initial_data["username"], code=code)
# except VerifyCode.DoesNotExist as e:
# pass
# except VerifyCode.MultipleObjectsReturned as e:
# pass
# 如果用get就需要这样,也可以except Exception as e捕获所有异常,但不好
verify_records = VerifyCode.objects.filter(mobile=self.initial_data["username"]).order_by("-add_time") # 先查询验证码,self.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是validate_code后的dict
"""
重载以用来解决code多余和mobile未填写的问题
"""
attrs["mobile"] = attrs["username"] # 前端的手机号输入框实际是username,没有这个的话,在执行is_valid的时候会抛异常
del attrs["code"]
return attrs
class Meta:
model = User # username是必填字段,因为User继承了django的AbstractUser类
fields = ("username", "code", "mobile", "password") # code本事不是User的字段,所以自己添加了code
# 比较好的办法是前端提供username和mobile两个的input框,都post过来
在users/views.py中编写一个UserViewSet类:
class UserViewSet(CreateModelMixin, mixins.UpdateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
"""
用户
"""
serializer_class = UserRegSerializer
queryset = User.objects.all()
引入serializer:
from .serializers import SmsSerializer, UserRegSerializer
并配置一个url,在根urls.py中:
from users.views import SmsCodeViewSet, UserViewSet
router.register(r'users', UserViewSet, base_name="users")
测试(通过数据库、xadmin快速添加验证码)。
错误提交,看一下提示格式:
http code
{
"字段1": ["错误1", "错误2"],
"字段2": ["错误1", "错误2"]
}