DRF的Serializer组件(源码分析)

2023-04-23 01:08:09 来源:博客园
DRF的Serializer组件(源码分析)1. 数据校验

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


(资料图)

对请求数据校验(底层调用Django的Form和ModelForm)对数据库查询到的对象进行序列化示例一: 基于Serializer
# models.pyclass UserInfo(models.Model):    username = models.CharField(verbose_name="用户名", max_length=32)    age = models.CharField(verbose_name="年龄", max_length=32)    level_choice = ((1, "VIP"), (2, "SVIP"), (3, "PARTNER"))    level = models.CharField(verbose_name="级别", choices=level_choice, max_length=32)    email = models.CharField(verbose_name="邮箱", max_length=32)
# views.py 基于Serializerclass UserSerializers(serializers.Serializer):    username = serializers.CharField(label="用户名", max_length=32)    age = serializers.CharField(label="年龄", max_length=32)    level = serializers.ChoiceField(label="级别", choices=models.UserInfo.level_choice)    email = serializers.CharField(label="用户名", min_length=6, max_length=32, validators=[EmailValidator, ])    email1 = serializers.CharField(label="用户名", min_length=6, max_length=32)    email2 = serializers.CharField(label="用户名", min_length=6, max_length=32)    def validate_email2(self, value):        """ 钩子函数, 用于验证某个字段 """        if re.match("^\w+@\w+\.\w+$", value):            return value        raise exceptions.ValidationError("邮箱格式错误") class UserView(APIView):    """ 用户管理 """    def post(self, request):        """ 添加用户 """        ser = UserSerializers(data=request.data)  # 将请求体数据传入, 这个request.data可以解析各种数据        if not ser.is_valid():            return Response({"code": 1006, "data": ser.errors})        print(ser.validated_data)        # 将数据保存到数据库        return Response({"code": 0, "data": "xxxx"})
示例二: 基于ModelSerializer
# models.pyfrom django.db import modelsclass Role(models.Model):    """ 角色表 """    title = models.CharField(verbose_name="名称", max_length=32)class Department(models.Model):    """ 部门表 """    title = models.CharField(verbose_name="名称", max_length=32)class UserInfo(models.Model):    username = models.CharField(verbose_name="用户名", max_length=32)    age = models.CharField(verbose_name="年龄", max_length=32)    level_choice = ((1, "VIP"), (2, "SVIP"), (3, "PARTNER"))    level = models.CharField(verbose_name="级别", choices=level_choice, max_length=32)    email = models.CharField(verbose_name="邮箱", max_length=32)    # 创建外键    depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE)    # 多对多    roles = models.ManyToManyField(verbose_name="角色", to="Role")
# views.py# 基于ModelSerializerclass UserModelSerializer(serializers.ModelSerializer):    email1 = serializers.CharField(label="邮箱1", validators=[EmailValidator, ])    class Meta:        model = models.UserInfo        fields = ["username", "age", "email", "email1", "roles"]  # 需要传入的数据, 多对多        extra_kwargs = {            "username": {"min_length": 4, "max_length": 32},            "age": {"max_length": 3}        }               def valicate_email(self, value):            ....            return valueclass UserView(APIView):    """ 用户管理 """    def post(self, request):        """ 添加用户 """        ser = UserModelSerializer(data=request.data)  # 将请求体数据传入, 这个request.data可以解析各种数据        if not ser.is_valid():            return Response({"code": 1006, "data": ser.errors})        print(ser.validated_data)        # 将数据保存到数据库        ser.validated_data.pop("email1")  # 删除不需要存入数据库的数据        ser.save(level=1, depart_id=1)  # 加入初始化数据        return Response({"code": 0, "data": "创建成功"})
2. 序列化示例一: 序列化基本字段
class UserModelSerializer(serializers.ModelSerializer):    class Meta:        model = models.UserInfo        fields = ["username", "age", "level", "email", "depart", "roles"]  # 序列化基本字段class UserView(APIView):    """ 用户管理 """    def get(self, request):        """ 序列化数据 """        queryset = models.UserInfo.objects.all()        ser = UserModelSerializer(instance=queryset, many=True)        print(ser.data)        return Response({"code": 0, "data": ser.data})

返回值:

HTTP 200 OKAllow: GET, HEAD, OPTIONSContent-Type: application/jsonVary: Accept{    "code": 0,    "data": [        {            "username": "ifeng",            "age": "11",            "level": 1,            "email": "ifeng190410@gmail.com",            "depart": 1,            "roles": []        },        {            "username": "Mcoco",            "age": "11",            "level": 1,            "email": "ifeng190410@gmail.com",            "depart": 1,            "roles": [                1,                2            ]        }    ]}
示例二: 自定义字段
from django.forms.models import model_to_dictfrom rest_framework import serializersfrom rest_framework.response import Responsefrom rest_framework.views import APIViewfrom api import modelsclass UserModelSerializer(serializers.ModelSerializer):    # 自定义字段    level_text = serializers.CharField(source="get_level_display")    depart = serializers.CharField(source="depart.title")    roles = serializers.SerializerMethodField()    extra = serializers.SerializerMethodField()    class Meta:        model = models.UserInfo        fields = ["username", "age", "level_text", "email", "depart", "roles", "extra"]    def get_roles(self, obj):        data_list = obj.roles.all()        return [model_to_dict(item, ["id", "title"]) for item in data_list]    def get_extra(self, obj):        return 666class UserView(APIView):    """ 用户管理 """    def get(self, request):        """ 序列化数据 """        queryset = models.UserInfo.objects.all()        ser = UserModelSerializer(instance=queryset, many=True)        print(ser.data)        return Response({"code": 0, "data": ser.data})

返回值:

{   "code": 0,   "data": [       {           "username": "ifeng",           "age": "11",           "level_text": "SVIP",           "email": "ifeng190410@gmail.com",           "depart": "后端",           "roles": [],           "extra": 666       },       {           "username": "Mcoco",           "age": "11",           "level_text": "VIP",           "email": "ifeng190410@gmail.com",           "depart": "销售",           "roles": [               {                   "id": 1,                   "title": "CEO"               },               {                   "id": 2,                   "title": "CFO"               }           ],           "extra": 666       }   ]}
示例三: 序列化类的嵌套

嵌套主要是面向外键和多对多表的时候

3. 数据校验&序列化

注意点:

​我们在做多对多数据校验的时候, 后面如果需要新增数据, 则需要重写create方法, 如果需要更新数据, 则需要重写update方法

# mdoels.pyfrom django.db import models# Create your models here.class Role(models.Model):    """ 角色表 """    title = models.CharField(verbose_name="名称", max_length=32)class Department(models.Model):    """ 部门表 """    title = models.CharField(verbose_name="名称", max_length=32)class UserInfo(models.Model):    username = models.CharField(verbose_name="用户名", max_length=32)    age = models.CharField(verbose_name="年龄", max_length=32)    level_choice = ((1, "VIP"), (2, "SVIP"), (3, "PARTNER"))    level = models.SmallIntegerField(verbose_name="级别", choices=level_choice)  # 类型为Int    email = models.CharField(verbose_name="邮箱", max_length=32)    # 创建外键    depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE)    # 多对多    roles = models.ManyToManyField(verbose_name="角色", to="Role")
# views.py# 数据校验&序列化class DepartModelSerializer(serializers.ModelSerializer):    class Meta:        model = models.Department        fields = ["id", "title"]        extra_kwargs = {            "id": {"read_only": False},  # 数据验证, 需传入id, 为后续的create做准备            "title": {"read_only": True}  # 序列化        }class RoleModelSerializer(serializers.ModelSerializer):    class Meta:        model = models.Role        fields = ["id", "title"]        extra_kwargs = {            "id": {"read_only": False},  # 数据校验, 需传入id, 为后续的create做准备            "title": {"read_only": True}  # 序列化        }class UserModelSerializer(serializers.ModelSerializer):    level_text = serializers.CharField(source="get_level_display", read_only=True)  # read_only -> 只序列化, 但是不数据校验    # Serializer嵌套,如果不设置read_only,一定要自定义create和update,自定义新增和更新的逻辑。    depart = DepartModelSerializer(many=False)    roles = RoleModelSerializer(many=True)    extra = serializers.SerializerMethodField(read_only=True)    email2 = serializers.EmailField(write_only=True)  # write_only -> 只数据校验不序列化    # 数据校验:username、email、email2、部门、角色信息    class Meta:        model = models.UserInfo        # username, age, email是即read_only也write_only        fields = [            "username", "age", "email", "level_text", "depart", "roles", "extra", "email2"        ]        # 给字段添加额外参数        extra_kwargs = {            "age": {"read_only": True},            "email": {"validators": [EmailValidator, ]},        }    def get_extra(self, obj):        return 666    def validate_username(self, value):  # 钩子方法        return value    # 新增加数据时, 因为无法解决m2m的储存问题. 所以需要重写create方法    def create(self, validated_data):        """        如果有嵌套的Serializer,在进行数据校验时,只有两种选择:              1. 将嵌套的序列化设置成 read_only              2. 自定义create和update方法,自定义新建和更新的逻辑            注意:用户端提交数据的格式。        """        """        validated_data:        OrderedDict([("username", "xiaoergu"), ("email", "xiaoergu@gmail.com"), ("depart", OrderedDict([("id", 2)])), ("roles", [OrderedDict([("id", 1)]), OrderedDict([("id", 2)])]), ("email2", "budianlong@gmail.com")])        """        depart_id = validated_data.pop("depart")["id"]  # 拿到depart的id        role_id_list = [ele["id"] for ele in validated_data.pop("roles")]  # 拿到roles的所有id        # 新增用户表        validated_data["depart_id"] = depart_id        user_object = models.UserInfo.objects.create(**validated_data)        # 在用户表和角色表的关联表中添加对应关系, django-orm知识        user_object.roles.add(*role_id_list)        return user_objectclass UserView(APIView):    """ 用户管理 """    def get(self, request):        """ 添加用户 """        queryset = models.UserInfo.objects.all()        ser = UserModelSerializer(instance=queryset, many=True)        return Response({"code": 0, "data": ser.data})    def post(self, request):        """ 添加用户 """        ser = UserModelSerializer(data=request.data)        if not ser.is_valid():            return Response({"code": 1006, "data": ser.errors})        print(ser.validated_data)        ser.validated_data.pop("email2")        instance = ser.save(age=18, level=3)        # 新增之后的一个对象(内部调用UserModelSerializer进行序列化)        print(instance)        # ser = UserModelSerializer(instance=instance, many=False)        # ser.data        return Response({"code": 0, "data": ser.data})

返回值:

4. 源码分析

底层源码实现:

序列化的底层源码实现有别于上述其他的组件,序列化器相关类的定义和执行都是在视图中被调用的,所以源码的分析过程可以分为:定义类、序列化、数据校验。

源码1:序列化过程

源码2:数据校验过程

关键词:

Copyright   2015-2032 华西社团网 版权所有  备案号:京ICP备2022016840号-35  联系邮箱: 920 891 263@qq.com