首页 > 技术文章 > 序列化和表单验证

lyq-biu 2018-09-11 10:14 原文

Django rest framework之序列化和表单验证

一.Serilalizer(定制性好,具体参考官网)

串行器允许诸如查询集和模型实例复杂的数据转换为可随后被容易地呈现到机Python数据类型JSONXML或其他内容类型。序列化程序还提供反序列化,允许在首次验证传入数据后将解析后的数据转换回复杂类型。

REST框架中的序列化程序与Django FormModelForm类的工作方式非常相似我们提供了一个Serializer类,它为您提供了一种强大的通用方法来控制响应的输出,以及一个ModelSerializer类,它提供了一个有用的快捷方式来创建处理模型实例和查询集的序列化程序。

每个序列化器字段类构造函数至少采用这些参数。某些Field类采用其他特定于字段的参数,但应始终接受以下内容:

read_only

只读字段包含在API输出中,但在创建或更新操作期间不应包含在输入中。任何错误包含在序列化程序输入中的“read_only”字段都将被忽略。

将其设置为True以确保在序列化表示时使用该字段,但在反序列化期间创建或更新实例时不使用该字段。

默认为 False

write_only

将其设置True为确保在更新或创建实例时可以使用该字段,但在序列化表示时不包括该字段。

默认为 False

required

通常,如果在反序列化期间未提供字段,则会引发错误。如果在反序列化期间不需要此字段,则设置为false。

将此设置为False还允许在序列化实例时从输出中省略对象属性或字典键。如果密钥不存在,它将不会包含在输出表示中。

默认为True

default

如果设置,则给出默认值,如果未提供输入值,将使用该字段。如果未设置,则默认行为是根本不填充该属性。

default过程中部分更新操作不适用。在部分更新的情况下,只有传入数据中提供的字段将返回一个验证值。

可以设置为函数或其他可调用函数,在这种情况下,将在每次使用时评估该值。调用时,它不会收到任何参数。如果callable有一个set_context方法,那么在每次获取值之前都会调用该方法,并将字段实例作为唯一参数。这与验证器的工作方式相同

序列化实例时,如果实例中不存在对象属性或字典键,则将使用默认值。

请注意,设置default值意味着不需要该字段。包含defaultrequired关键字参数都是无效的,并且会引发错误。

allow_null

通常,如果None传递给序列化程序字段,则会引发错误将此关键字参数设置为Trueif None应被视为有效值。

请注意,如果没有显式default,将此参数设置为True将暗示序列化输出defaultnull,但不暗示输入反序列化的默认值。

默认为 False

source

将用于填充字段的属性的名称。可能是一个只接受self参数的方法,例如URLField(source='get_absolute_url'),或者可以使用点分表示来遍历属性,例如EmailField(source='user.email')使用点分表示法序列化字段时,default如果任何对象不存在或在属性遍历期间为空,则可能需要提供值。

该值source='*'具有特殊含义,用于指示整个对象应该传递到该字段。这对于创建嵌套表示或者需要访问整个对象以确定输出表示的字段非常有用。

默认为字段名称。

validators

应该应用于传入字段输入的验证程序函数列表,它会引发验证错误或只是返回。验证器函数通常应该提高serializers.ValidationError,但是Django的内置函数ValidationError也支持与Django代码库或第三方Django软件包中定义的验证器兼容。

error_messages

错误消息的错误代码字典。

label

一个简短的文本字符串,可用作HTML表单字段或其他描述性元素中字段的名称。

help_text

一个文本字符串,可用作HTML表单字段或其他描述性元素中字段的描述。

initial

应该用于预填充HTML表单字段值的值。你可以将一个callable传递给它,就像你对任何常规Django一样Field

import datetime
from rest_framework import serializers
class ExampleSerializer(serializers.Serializer):
    day = serializers.DateField(initial=datetime.date.today)

style

键值对的字典,可用于控制渲染器应如何渲染字段。

这里的两个例子是'input_type''base_template'

# Use <input type="password"> for the input.
password = serializers.CharField(
    style={'input_type': 'password'}
)

# Use a radio input instead of a select input.
color_channel = serializers.ChoiceField(
    choices=['red', 'green', 'blue'],
    style={'base_template': 'radio.html'}
)
class SmsSerializer(serializers.Serializer):
    '''
    注册手机号和验证码序列化
    '''
    mobile = serializers.CharField(max_length=11,min_length=11)

    def validate_mobile(self, mobile):
        '''
        验证手机号码
        '''
        # 验证手机是否合法
        if not re.match(REGEX_MOBILE, mobile):
            raise serializers.ValidationError('手机号非法')
        # 手机是否注册
        if User.objects.filter(mobile=mobile).count():
            raise serializers.ValidationError('用户已经存在')
        on_minute_ago = datetime.now() - timedelta(hours=0, minutes=1, seconds=0)
        if VerifyCode.objects.filter(add_time__gt=on_minute_ago, mobile=mobile):
            raise serializers.ValidationError('距离上一次发送未超过60秒')
        return mobile

 

 

二.ModelSerializer

class UserRegSerializer(serializers.ModelSerializer):
    '''
    用户注册序列化
    '''
    # write_only=True,不会拿该字段来序列化,labe标签名,help_text:docs文档中description
    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='验证码')
    username = serializers.CharField(required=True, allow_blank=False,
                                     validators=[UniqueValidator(queryset=User.objects.all(), message='用户已经存在')])
    #style密文,write_only=True不返回,保存为明文
    password = serializers.CharField(style={"input_type":"password"},write_only=True)
    #重载create函数,保存为密文,该方法可以实现加密密码,还可以信号(分离性好)
    # 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 validate_code(self, code):
        # 不用get,如果返回两条数据以上,会抛异常
        # try:
        #   verify_codes=VerifyCode.objects.get(mobile=self.initial_data['username'])
        # except VerifyCode.DoesNotExist as e:
        #     pass
        # except VerifyCode.MultipleObjectsReturned as e:
        #     pass
        verify_codes = VerifyCode.objects.filter(mobile=self.initial_data['username']).order_by('-add_time')
        if verify_codes:
            last_verfycode = verify_codes[0]
            five_minute_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
            if five_minute_ago > last_verfycode.add_time:
                raise serializers.ValidationError('验证码过期')
            if code != last_verfycode.code:
                raise serializers.ValidationError('验证码错误')
        else:
            raise serializers.ValidationError('验证码错误')

    # 作用于所有字段,将验证码删除,不是插入数据库的字段
    def validate(self, attrs):
        attrs["mobile"] = attrs["username"]
        del attrs["code"]
        return attrs

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

三.动态设置Serializer(重载GenericAPIView中的get_serializer_class方法)

class UserViewset(mixins.CreateModelMixin,mixins.UpdateModelMixin,mixins.RetrieveModelMixin,viewsets.GenericViewSet):
    '''
    用户
    '''
    serializer_class = UserRegSerializer
    queryset = User.objects.all()
    authentication_classes = (JSONWebTokenAuthentication,authentication.SessionAuthentication)
    #更新,添加用户信息放在一起,是否登录应该动态,注册不用登录IsAuthenticated,该方法不行
    # permission_classes = (permissions.IsAuthenticated)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user=self.perform_create(serializer)
        re_dict=serializer.data
        payload=jwt_payload_handler(user)
        re_dict['token']=jwt_encode_handler(payload)
        re_dict['name']=user.name if user.name else user.username
        headers = self.get_success_headers(serializer.data)
        return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)

    def get_serializer_class(self):
        '''
        重载GenericAPIView中的get_serializer_class函数,调用不同的序列化类,如果是create,
        就调用UserRegSerializer序列化,否则UserDetailSerializer序列化
        :return: 
        '''
        if self.action == 'retrieve':
            return UserDetailSerializer
        elif self.action == 'create':
            return UserRegSerializer
        return UserDetailSerializer

    def get_permissions(self):
        '''
        重载APIview中的get_perimissions函数,如果是新增用户则不用登录,否则必须登录
        :return: 
        '''
        if self.action == 'retrieve':
            return [permissions.IsAuthenticated()]
        elif self.action == 'create':
            return []
        return []

    def get_object(self):
        '''
        返回当前用户
        :return: 
        '''
        return self.request.user

    def perform_create(self, serializer):
        return serializer.save()

 

推荐阅读