7.10.serializer和validator验证

serializers

作用

  1. 控制返回体中包括哪些字段

  2. 检验请求体中字段的有效性,当校验失败时返回错误信息

  3. 数据格式转换

    序列化:允许将复杂对象序列化为易于渲染成JSON、XML原生python数据类型

    反序列化:在数据校验通过后,将json、xml等数据反序列化为复杂对象

类型

serializers

控制返回体中包括哪些字段

class GoodsCategorySerializer(serializers.Serializer):
    name = serializers.CharField()
    code = serializers.CharField()
    desc = serializers.CharField()
    category_type = serializers.IntegerField()

检验请求体中字段的有效性,当校验失败时返回错误信息

自定义创建和更新逻辑

from rest_framework import serializers

from goods.models import Goods
from trade.models import ShoppingCart


class ShopCartSerializer(serializers.Serializer):
    user = serializers.HiddenField(default=serializers.CurrentUserDefault())
    nums = serializers.IntegerField(required=True, min_value=1,
                                    error_messages={
                                        'min_value': '商品数量不能小于1',
                                        'required': '请选择购买数量'
                                    }
                                    )
    goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all())

    # 自定义创建逻辑
    def create(self, validated_data):
        # 获取用户信息
        user = self.context['request'].user
        nums = validated_data['nums']
        goods = validated_data['goods']

        existed = ShoppingCart.objects.filter(user=user, goods=goods)

        if existed:
            existed = existed[0]
            existed.nums += nums
            existed.save()
        else:
            existed = ShoppingCart.objects.create(**validated_data)
        return existed

    # 自定义更新逻辑
    def update(self, instance, validated_data):
        instance.nums = validated_data['nums']
        instance.save()
        return instance

modelSerializer

ModelSerializer简化了Serializer的使用,但存在一定的局限性。比如Model中要求必须存在的字段而Form中没有;再比如Form中需要校验的字段而Model中没有

控制返回体中包括哪些字段

goods/serializer.py

class GoodsCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategory
        fields = ('name', 'code', 'desc', 'add_time')

若需要取出所有字段使用fields = '__all__'

检验请求体字段,当校验失败时返回错误信息

详见:《validator校验器》小节

常见问题

GET请求:如何向response中添加model中没有的数据?

参见Django-drf给序列化对象添加额外字段信息

POST请求:如何向表单中添加额外的数据?

同Serializer,使用code = serializers.CharField()的方式声明额外数据, 并向fields中添加

示例:User中没有code字段,向注册表单中添加code字段的方法如下:

class UserRegSerializer(serializers.ModelSerializer):
    code = serializers.CharField(...)

    def validate(self, attrs):
        ...
        # 若提交的数据中存在不属于Model中的数据,将会报错 
        # UserProfile() got an unexpected keyword argument 'code'
        # 解决方式:重写validate方法,通过del attrs["code"] 删除额外的数据
        del attrs["code"]
        return attrs

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

POST请求:某些字段表单中存在,但表单存储后不想返回怎么办?

使用write_only=True声明该字段仅允许被写入,如下:

password = serializers.CharField(style={'input_type': 'password'}, help_text="密码", label="密码", write_only=True)

完整示例

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='验证码')

    def validate(self, attrs):
        attrs["mobile"] = attrs["username"]
        attrs["password"] = make_password(attrs["password"])
        del attrs["code"]
        return attrs

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

validator校验器

校验规则可能来自model或serializer

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='验证码')
    username = serializers.CharField(label="用户名", help_text="用户名", required=True, allow_blank=False,
                                     validators=[UniqueValidator(queryset=User.objects.all(), message="用户已经存在")])

    password = serializers.CharField(style={'input_type': 'password'}, help_text="密码", label="密码", write_only=True)

    # 对单一字段进行自定义校验
    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_minutes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
            if five_minutes_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["mobile"] = attrs["username"]
        # attrs["password"] = make_password(attrs["password"]) # 对密码加密,方式一
        del attrs["code"]
        return attrs

    def create(self, validated_data):
        # 对密码加密,方式一
        user = super(UserRegSerializer, self).create(validated_data=validated_data)
        user.set_password(validated_data["password"])
        user.save()
        return user
    class Meta:
        model = User
        fields = ('username', 'code', 'mobile', 'password')

Last updated

Was this helpful?