传统的数据库驱动的网站应用,说简单了就是数据库的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金融量化。致力于使用最前沿的认知技术去理解这个复杂的世界。
|