传统的数据库驱动的网站应用,说简单了就是数据库的CURD(增删改查)。其中前台“查“相关居多。后台增、删、改居多。
有一个模块通用且例外,就是用户注册和登录过程。注册本质上是新增一个用户,但有校验,验证码之类的过程。在前后端分离的应用里,就是向后台新建一个用户。

在restframework的框架里,两个最常用的模块,一是序列化,二是viewset。这个很好理解,平时开发里request取出参数,然后orm查询返回queryset,queryset要”序列化“为json串,然后传给前端。这里serializers就干这个。而viewsets就是定义CURD的行为
from rest_framework import serializers,viewsets
viewsets里,有一个最常用的ModelViewSet。我们让UserViewSet直接继承ModelViewSet。
普通的增删改查、列表应用,只需要定义一个序列化器,然后继承一个ModelViewSet就可以了。
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username','date_joined', 'email', 'is_staff')

# ViewSets define the view behavior.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer


看下ModelViewSet的源代码,其实就是扩展了CURDL:
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
   A viewset that provides default `create()`, `retrieve()`, `update()`,
   `partial_update()`, `destroy()` and `list()` actions.
   """
   pass
而CRUDL主要是利用序列化器作了相应的操作,比如校验各种字段,然后进行增删改查操作。
如果仅需要提供查询功能,比如博客列表和详情页。(很多增删改都是后台功能,django的admin自动就完成了)
class ArticleListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
这样就可以了。

本文重点说说user-auth也就是登录注册。传统的create肯定不能直接用,因为django的password不是明文保存的。

对于创建用户,只需要提供username,password,但这里还有一项,不是model里有的,就是手机验证码。
在我们自定义的序列化类里,可以加上code字段,以及相应的一些验证逻辑。
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="验证码")

class Meta:
model = User
fields = ("username", "password", "code")

最大的扩展,在serilizer上,序列化的过程,会检查输入字段合法性,对各字段进行加工处理,然后做CURD操作后返回。

默认我们可以绑定要操作的model,这样就不用一个个写字段。但注册过程就有几种默认无法完成的工作。

1,手机号注册,需要验证短信码。
user的model里没有短信码的,所以需求加一个code字段。当然可以配置各种检验规则和错误提示,返回里也没有这个短信码,这个字段是write_only。
2,密码输入框不应该是明文的,同时,用户注册成功,不应该把密码字段返回。所以密码字段是write_only。
#确保这个字段按密码显示,且创建后不返回(write_only)
password = serializers.CharField(
style={'input_type': 'password'}, help_text="密码", label="密码", write_only=True,
)
3,创建过程,需要对username排重了。如果已存在,会返回错误。
#创建的时候,会使用username排重,确保没有已注册
username = serializers.CharField(label="用户名", help_text="用户名", required=True, allow_blank=False,
validators=[UniqueValidator(queryset=User.objects.all(), message="用户已经存在")])
4,验证格不仅仅是格式检验,还需要与发送出去的验证码,做时间验证:
def validate_code(self, code):
   verify_records = VerifyCode.objects.filter(mobile=self.initial_data["username"]).order_by("-add_time")
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("验证码错误")
5,用户密码在django里不是明文保存的,需要重载create/update两个函数
这个过程,也可以用信号量来做,这里先不展开了。
#重载create/update,加密密码
def create(self, validated_data):
user = super(UserRegSerializer, self).create(validated_data=validated_data)
user.set_password(validated_data["password"])
user.save()
return user

def update(self, instance, validated_data):
user = super(UserRegSerializer, self).update(instance=instance,validated_data=validated_data)
user.set_password(validated_data["password"])
user.save()
return user
如上,就完成了django,django-rest-framework手机号注册,发送验证码,检验并注册成功的后台。

用户注册的提交过程,基本上可以算是django里create过程最复杂的了。其他的model就是一些基础校验比如不能为空,不能超长等。一般不做内容检验,input/output过程也没有限制。

关于作者:魏佳斌,互联网产品/技术总监,北京大学光华管理学院(MBA),特许金融分析师(CFA),资深产品经理/码农。偏爱python,深度关注互联网趋势,人工智能,AI金融量化。致力于使用最前沿的认知技术去理解这个复杂的世界。
扫描下方二维码,关注:AI量化实验室(ailabx),了解AI量化最前沿技术、资讯。

本篇文章使用有道云笔记编辑 点击体验