Bootstrap

Drf框架 Serializer详解

目录

1、数据校验(Serializer)

1、1 示例1:基于Serializer:

1、2 示例2:基于ModelSerializer:

1、3 示例3:基于ModelSerializer(含FK+M2M):

2 、序列化

2、1 示例1:序列化基本字段

2、2 示例2:自定义字段

2、3 示例3:序列化类的嵌套

3 、数据校验&序列化

4、序列化的过程

1、单个序列化post或get(单个)请求(UserModelSerializer(data=request.data))

2、多个序列化get请求(UserModelSerializer(instance=queryset, many=True))

5、序列化过程中request的处理

6、校验过程中校验参数的处理

7、read_only和write_only区别


drf中为我们提供了Serializer,他主要有两大功能:

  • 对请求数据校验(底层调用Django的Form和ModelForm)

  • 对数据库查询到的对象进行序列化

1、数据校验(Serializer)

1、1 示例1:基于Serializer:

import re
from django.core.validators import EmailValidator
from rest_framework import exceptions
from rest_framework import serializers



class RegexValidate:
    def __init__(self, base):
        self.base = base

    # 类被调用时,执行改方法
    def __call__(self, value):
        macth_obj = re.match(self.base, value)
        if not macth_obj:
            raise serializers.ValidationError("格式错误")


class MySerializers(serializers.Serializer):
    username = serializers.CharField(label="姓名", max_length=10, min_length=5, required=True)
    age = serializers.IntegerField(label="年龄", max_value=100, min_value=0, required=True)
    level=serializers.ChoiceField(label="级别",choices=models.UserInfo.level_choices)
    # 邮箱校验方式一
    email1 = serializers.EmailField(label="邮箱校验方式一")

    # 邮箱校验方式二
    email2 = serializers.CharField(label="邮箱校验方式二", validators=[RegexValidate(r"^\w+@\w+\.\w+$"), ])

    # 邮箱校验方式三
    email3 = serializers.CharField(label="邮箱校验方式三")

    def validate_email3(self, value):
        if re.match(r"^\w+@\w+\.\w+$", value):
            return value
        raise exceptions.ValidationError("格式错误")

1、2 示例2:基于ModelSerializer:

import re
from django.core.validators import EmailValidator
from rest_framework import exceptions
from rest_framework import serializers


class RegexValidate:
    def __init__(self, base):
        self.base = base

    # 类被调用时,执行改方法
    def __call__(self, value):
        macth_obj = re.match(self.base, value)
        if not macth_obj:
            raise serializers.ValidationError("格式错误")


class MySerializers(serializers.ModelSerializer):
    age = serializers.IntegerField(label="年龄", max_value=100, min_value=0, required=True)
    level = serializers.ChoiceField(label="级别", choices=models.UserInfo.level_choices)
    # 邮箱校验方式一
    email1 = serializers.EmailField(label="邮箱校验方式一")
    # 邮箱校验方式二
    email2 = serializers.CharField(label="邮箱校验方式二", validators=[RegexValidate(r"^\w+@\w+\.\w+$"), ])
    # 邮箱校验方式三
    email3 = serializers.CharField(label="邮箱校验方式三")

    class Meta:
        model = models.UserInfo
        fields = ["username", "age", "level", "email1", "email2", "email3"]
        extra_kwargs = {
            "username": {"max_length": 10, "min_length": 5}
        }

    def validate_email3(self, value):
        if re.match(r"^\w+@\w+\.\w+$", value):
            return value
        raise exceptions.ValidationError("格式错误")

1、3 示例3:基于ModelSerializer(含FK+M2M):

    class Meta:
        model = models.UserInfo
        fields = ["level", "roles"]
        extra_kwargs = {
            "username": {"max_length": 10, "min_length": 5}
        }

参考:Meta:是MySerializers(serializers.ModelSerializer)下定义的

这是你在传参时,必须带上level和roles,这是你就必须要用json格式来发送数据

{"level":1,"roles",[1,2]}

2 、序列化

通过ORM从数据库获取到的 QuerySet 或 对象 均可以被序列化为 json 格式数据。

2、1 示例1:序列化基本字段

class DepartModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Department
        fields = ['id', "title"]
# 切记, 如果从数据库获取的不是QuerySet对象,而是单一对象,例如:
data_object = modes.UserInfo.objects.filter(id=2).first()
ser = UserModelSerializer(instance=data_object,many=False)
print(ser.data)

2、2 示例2:自定义字段

class NewsSerializer(serializers.ModelSerializer):
    image_list = serializers.SerializerMethodField()
    topic_title = serializers.CharField(source="topic.title",)
    zone_title = serializers.CharField(source="get_zone_display", )
    status = serializers.CharField(source="get_status_display",)

    class Meta:
        model = models.News
        fields = ['id', "title", "url",
                  'image', 'topic', "zone",
                  "zone_title", 'image_list', "topic_title", 'collect_count', 'recommend_count', 'comment_count',
                  "status"]

    def get_image_list(self, obj):
        if not obj.image:
            return []
        return obj.image.split(',')

    def validate_topic(self, value):
        if not value:
            return value
        request = self.context['request']
        exists = models.Topic.objects.filter(deleted=False, id=value.id, user=request.user).exists()
        if not exists:
            raise ValidationError("话题不存在")
        return value

    def validate_title(self, value):
        url = self.initial_data.get('url')
        image = self.initial_data.get('image')
        zone = self.initial_data.get('zone')

        if url and image:
            raise ValidationError("请求数据错误")
        if not url and not image:
            if zone == 3:
                raise ValidationError("分区选择错误")
        return value

2、3 示例3:序列化类的嵌套

from rest_framework import serializers

#嵌套1
class IndexSubTopicSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Topic
        fields = ['id', 'title', 'is_hot']


#嵌套2
class IndexSubUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = ['id', 'username', ]


class IndexSerializer(serializers.ModelSerializer):

    topic = IndexSubTopicSerializer(source=topic)
    user = IndexSubUserSerializer(source=user)

    class Meta:
    fields=["topic","user"]

注意:在嵌套序列化的过程中,后面需要加一个参数,source=当前的对象,如下:

# demo.py   
 topic = IndexSubTopicSerializer(source=topic)
 user = IndexSubUserSerializer(source=user)

3 、数据校验&序列化

上述示例均属于单一功能(要么校验,要么序列化),其实当我们编写一个序列化类既可以做数据校验,也可以做序列化,例如:        

from rest_framework import serializers
class NewsSerializer(serializers.ModelSerializer):
    image_list = serializers.SerializerMethodField(read_only=True)
    topic_title = serializers.CharField(source="topic.title", read_only=True)
    zone_title = serializers.CharField(source="get_zone_display", read_only=True)
    status = serializers.CharField(source="get_status_display", read_only=True)

    # title、url、image、'topic', "zone"
    #   - 只有title,只创建文本 + 分区不能是图片
    #   - 有title,image,
    #   - 有title,url
    class Meta:
        model = models.News
        fields = ['id', "title", "url",
                  'image', 'topic', "zone",
                  "zone_title", 'image_list', "topic_title", 'collect_count', 'recommend_count', 'comment_count',
                  "status"]
        read_only_fields = ['collect_count', 'recommend_count', 'comment_count']
        extra_kwargs = {
            'topic': {'write_only': True},  # 新增时,topic=1
            'image': {'write_only': True},  # 图片地址   xxxx,xxxx,xxxx
            'zone': {'write_only': True},
        }

    def get_image_list(self, obj):
        if not obj.image:
            return []
        return obj.image.split(',')

    def validate_topic(self, value):

        return value

    def validate_title(self, value):
        
        return value

注意:

1、read_only=True时,不用校验,必须序列化返回

2、write_only=True时,必须校验,不用序列化返回

3、read_only_fields来制定必须序列化的字段

4、序列化的过程

1、单个序列化post或get(单个)请求(UserModelSerializer(data=request.data))

    def post(self, request):
        """ 添加用户 """
        ser = UserModelSerializer(data=request.data)
        if not ser.is_valid():
            return Response({'code': 1006, 'data': ser.errors})

        ser.validated_data.pop('email2')

        instance = ser.save(age=18, password="123", depart_id=1)

        # 新增之后的一个对象(内部调用UserModelSerializer进行序列化)
        print(instance)
        # ser = UserModelSerializer(instance=instance, many=False)
        # ser.data

        return Response({'code': 0, 'data': ser.data})

save()会返回一个obj对象

2、多个序列化get请求(UserModelSerializer(instance=queryset, many=True))

    def get(self, request):
        """ 添加用户 """
        queryset = models.UserInfo.objects.all()
        ser = UserModelSerializer(instance=queryset, many=True)
        ser.data #序列化后的数据
        return Response({"code": 0, 'data': ser.data})

5、序列化过程中request的处理

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        kwargs.setdefault('context', self.get_serializer_context())
        return serializer_class(*args, **kwargs)

#将request添加到context中
    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self
        }

#servalisers类,将context提取到自己的类中
    def __init__(self, instance=None, data=empty, **kwargs):
        self.instance = instance
        if data is not empty:
            self.initial_data = data
        self.partial = kwargs.pop('partial', False)
        self._context = kwargs.pop('context', {})
        kwargs.pop('many', None)
        super().__init__(**kwargs)

在序列化类中可以通过:

request=self.context["request"]来获取

    def validate_topic(self, value):
        if not value:
            return value
        request = self.context['request']
        exists = models.Topic.objects.filter(deleted=False, id=value.id, user=request.user).exists()
        if not exists:
            raise ValidationError("话题不存在")
        return value

6、校验过程中校验参数的处理

    def validate_title(self, value):
        url = self.initial_data.get('url')
        image = self.initial_data.get('image')
        zone = self.initial_data.get('zone')

        if url and image:
            raise ValidationError("请求数据错误")
        if not url and not image:
            if zone == 3:
                raise ValidationError("分区选择错误")
        return value

通过:

1、url = self.initial_data.get('url')来提取需要校验的值(在ser.is_valid()过程中

2、ser = UserModelSerializer(data=request.data)

 ser.is_valid()校验完成后

ser.validated_data才会有校验的数据

7、read_only和write_only区别

1、read_only只会走instance参数,返回数据,不会走data参数(is_valid),只管返回的事情

1、可以在自定义字段时加入read_only=True,

2、read_only_fields=[]里批量添加,

3、extra_kwargs里设置{"xx":{"read_only":True}}

2、write_only只会走data=(is_valid)参数,校验数据,不会走instance参数,

校验完成后,

ser.save()添加一个对象实例,返回当前对象,

ser.data获取返回的对象实例序列化后的数据(注意:instance=ser,many=False)

;