还是和之前的开发一个API功能的步骤一样:
from rest_framework.generics import CreateAPIView
from .models import User
from .serializers import UserModelSerializer
class UserAPIView(CreateAPIView):
"""用户信息视图"""
#这里的queryset可加可不加
queryset = User.objects.all()
serializer_class = UserModelSerializer
这个注册视图继承的是CreateAPIView,看了一下源码,CreateAPIView里面已经实现好post方法了,相当于是专门拿来做新建数据的API。继承了它,我们不需要在UserAPIView中重写post方法。然后是定义queryset,其实这里加不加都不影响,因为是post方法。最后定义序列化器。
得新建一个文件serializers.py,里面的代码如下:
from rest_framework import serializers
from .models import User
import re
from .utils import get_user_by_account
from django.contrib.auth.hashers import make_password
class UserModelSerializer(serializers.ModelSerializer):
sms_code = serializers.CharField(min_length=4, max_length=6, required=True, write_only=True, help_text="短信验证码")
token = serializers.CharField(max_length=1024, read_only=True, help_text="token认证字符串")
class Meta:
model = User
fields = ['id', 'username', 'token', 'mobile', 'sms_code', 'password']
extra_kwargs = {
"mobile":{
"write_only": True,
},
"username":{
"read_only": True,
},
"id":{
"read_only": True,
},
"password":{
"write_only": True,
}
}
def validate(self, attrs):
mobile = attrs.get("mobile")
sms_code = attrs.get("sms_code")
password = attrs.get("password")
# 验证手机号码的格式
if not re.match("^1[3-9]\d{9}", mobile):
raise serializers.ValidationError("对不起,手机号码格式有误")
# 验证手机号是否已经注册过
ret = get_user_by_account(mobile)
if ret is not None:
raise serializers.ValidationError("对不起,手机已经被注册")
# todo 验证短信验证码是否正确
return attrs
def create(self, validated_data):
"""保存用户信息"""
validated_data.pop("sms_code") #移除掉不需要的数据
#对密码进行加密
raw_password = validated_data.get("password")
hash_password = make_password(raw_password)
#对用户名设置一个默认值
username = validated_data.get("mobile")
#调用序列化器提供的create方法
# super().create()
user = User.objects.create(
mobile=username,
username=username,
password=hash_password,
)
from rest_framework_jwt.settings import api_settings
# 使用restframework.jwt提供手动生成token的方法生成登录状态
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
user.token = jwt_encode_handler(payload)
return user
之前有提到过,序列化器中可以完成:字段声明、模型序列化器声明、验证方法、存储数据方法,4个功能。注册API中全部都用到了。
因为sms_code和token是User表中不存在的字段,因此我们需要在序列化器中声明。Meta是内部类,指定所用的模型表和需要序列化的字段。之所以我们post的时候需要传三个值(mobile, sms_code, password),成功插入之后返回(id, username, token),是因为我们设置了每个值的write_only和read_only字段,write_only为True表示只写,只能通过post发过来,read_only为True表示只读,将结果返回回去。
我们还需要对手机号码进行验证,所以重写了validate方法,使用正则检查手机号的格式是否正确,在数据库中查找手机号是不是已经注册过,只有条件都符合了才进行下一步,否则抛出异常。
注册post还希望在我们验证成功之后对用户的信息进行保存,所以重写create方法。因为在数据模型中不存在sms_code这个字段,所以我们使用set的pop方法将其删除。密码不能明文存储,所以调用内置的make_password对密码进行加密。使用User.objects.create方法将准备好的数据插入数据库。我们还希望用户通过注册之后不需要在去登录,所以要返回一个token,使用rest_framework_jwt生成token,返回。
path(r'reg/', views.UserAPIView.as_view()),
至此,就完成了用户发送post请求之后,验证手机号码,存储用户信息,生成token,返回的注册API功能。