首页 > 编程语言 >DRF之序列化器【2】源码流程

DRF之序列化器【2】源码流程

时间:2024-12-26 12:56:55浏览次数:5  
标签:__ field fields self instance 源码 序列化 DRF

目录

前言

序列化器是Django框架中的一个重要概念,用于在Python对象和JSON等格式之间进行相互转换。通过序列化器,我们可以方便地将模型实例转换为JSON格式的数据,并且还可以对数据进行验证、创建和更新等操作。

上一篇文章我们讲解了序列化器对数据的序列化功能
点此快速跳转-DRF之序列化器【1】序列化
温馨提示:序列化器源码中涉及元类的知识,如果对其不了解,建议翻看往期有关元类的介绍
点此快速跳转-python中的元类

本文我们从源码的角度分析序列化器对数据的序列化实现

1. 流程概述

此处只做粗略的介绍,看不懂没关系,建议看完后面的源码解析后再回来梳理一遍大致流程

第一步:加载字段,创建类

  • class BaseSerializer(serializers.Serializer):
        xx = serializers.CharField(source="name")
    
    1. 在类成员中提取出xx字段并删除
    1. 汇总到 BaseSerializer._declared_fields = {"xx":对象}
  • class UserSerializer(serializers.ModelSerializer, BaseSerializer):
        yy = serializers.CharField(source="name")
        
        class Meta:
            model = models.UserInfo
            fields = ['name', 'age', "yy" ,"xx"]
    
    1. 在类成员中提取出yy字段并删除
    2. 将自己即继承类中的字段都汇总到UserSerializer._declared_fields = {"yy":对象,"xx":对象,"name":对象...}

第二步:序列化

  • queryset = models.UserInfo.objects.all()
    ser = UserSerializer(queryset, many=True)  # ListSerizlizer对象。循环queryset中的每一个对象,对其调用UserSerializer进行实例化
    
  • db->queryset = [{id:xxx,name:xxx,age:xxx},{id:xxx,name:xxx,age:xxx},]

  • instance = models.UserInfo.objects.all().first()
    ser = UserSerializer(instance, many=False)  # UserSerializer对象
    
  • db->instance = {id:xxx,name:xxx,age:xxx}

  • 序列化过程

    • instance = models.UserInfo.objects.all().first()
    • ser = UserSerializer(instance, many=False)
    • 当执行ser.data时触发
      • 内部寻找对应关系
        • 将UserSerializer._declared_fields中的字段与从数据库中的查到的数据的模型类字段一一对应
      • 逐一进行序列化

2. 创建字段对象

首先来思考一个问题

class Foo(object,metaclass=type):
    v1 = 123

    def func(self):
        return 999

上述代码的类,在创建时,是先加载字段v1和func还是先创建Foo这个类

如果看不出来,我们不妨换个形式

Foo = type("Foo", (object,), {"v1": 123, "func": lambda self: 999})

现在想必很清晰了,一定是先加载完v1,func这两个字段,才能把他们当成参数传递给type去创建Foo类

因此对于下列代码

class InfoSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField()
    order = serializers.IntegerField

一定也是先加载id,title,order三个字段即实例化IntegerField()等等对象…

所以我们先来看DRF创建字段对象的源码实现(精简版,用于理解实现逻辑)

class Field:
    _creation_counter = 0

    def __init__(self, ...):
        self._creation_counter = Field._creation_counter
        Field._creation_counter += 1


class IntegerField(Field):
    def __init__(self, **kwargs):
        self.max_value = 111
        super().__init__(**kwargs)
        
        
class CharField(Field):
    def __init__(self, **kwargs):
        self.allow_blank = Flase
        super().__init__(**kwargs)


class InfoSerializer(serializers.Serializer):
    id = serializers.IntegerField()  # {max_value:111, _creation_counter:0}
    title = serializers.CharField()  # {allow_blank:False, _creation_counter:1}
    order = serializers.IntegerField  # {max_value:111, _creation_counter:2}

id = serializers.IntegerField()在执行时,会实例化一个IntegerField对象,先调用自己的__init__方法给自己的实例空间写入值,如max_value,再调用父类Field__init__方法,实现_creation_counter=0,同时将Field的类变量+1,即Field._creation_counter = 1

title = serializers.CharField()在执行时,会实例化一个CharField对象,逻辑同上,但是他的_creation_counter=1Field._creation_counter = 2

order = serializers.IntegerField同理,_creation_counter=2Field._creation_counter = 3

如果还有其他XXSerializer类,他的字段也会接着给自己的_creation_counter赋值并且使类变量自增

到这里想必大家都意识到了,Field类相当于维护了一个计数器,给每一个字段赋予了创建的顺序,意义何在?

就是让后续开发时,根据字段创建的顺序,来决定源码处理的顺序(python3.6之前字典是无序的,因此需要这样一个逻辑)

所以想让那个字段先处理,就把他的位置写的上面一些,例如密码password与确认密码confirm_password就需要这样一个先后关系

3. 创建类

上一小节中我们已经了解了字段对象的创建逻辑,那么在字段加载完成后,字段将会被作为参数传递进去创建InfoSerializer

在这里插入图片描述

InfoSerializer类的父类是Serializer类,该类在创建时指定了metaclass=SerializerMetaclass,由元类的知识我们知道,一旦Serializer指定了metaclass=SerializerMetaclass,那么其所有的子类在创建时都将指定元类为SerializerMetaclass,因此不管是继承的Serializer还是ModelSerializer,最终都由元类SerializerMetaclass创建

下面我们就来看看SerializerMetaclass是怎么创建类的

class SerializerMetaclass(type):
    @classmethod
    def _get_declared_fields(cls, bases, attrs):
        # 1.1列表推导式构建自己类的字段列表fields
        fields = [(field_name, attrs.pop(field_name))
                  for field_name, obj in list(attrs.items())
                  if isinstance(obj, Field)]
        # 1.2排序
        fields.sort(key=lambda x: x[1]._creation_counter)

        # Ensures a base class field doesn't override cls attrs, and maintains
        # field precedence when inheriting multiple parents. e.g. if there is a
        # class C(A, B), and A and B both define 'field', use 'field' from A.
        known = set(attrs)

        def visit(name):
            known.add(name)
            return name

        # 1.3 列表推导式构建父类的字段列表base_fields
        base_fields = [
            (visit(name), f)
            for base in bases if hasattr(base, '_declared_fields')
            for name, f in base._declared_fields.items() if name not in known
        ]
        # 1.4 将父类字段与自己的字段合并列表返回
        return OrderedDict(base_fields + fields)

    def __new__(cls, name, bases, attrs):
        # 1.在attrs中新增_declared_fields字段用于存储自己的字段对象和父类的字段对象
        attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
        # 2.再调用type的__new__创建类
        return super().__new__(cls, name, bases, attrs)


# 该类在创建时attr一共是五个值,一个整型三个字段对象和一个类
class InfoSerializer(serializers.Serializer):
    v1 = 123
    id = serializers.IntegerField()
    title = serializers.CharField()
    order = serializers.IntegerField

    class Meta:

  • 1.1 列表推导式构建自己类的字段列表fields
attrs -->  {"v1":123,"id":IntegerField对象,"title":CharField对象,...}

循环获取字段名field_name和字段值obj,如果objField的子类,则构建元组加入fields列表,同时在attrs中剔除,

因此循环结束后

attrs -->  {"v1":123,"Meta":对象}
fields = [("id",对象),("title",对象),("order",对象)]
  • 1.2 排序

还记得上一节创建字段对象时的那个计数器吗?这一步的排序便是依据各个字段对象内的_creation_counter大小进行排序

  • 1.3 列表推导式构建父类的字段列表base_fields

循环父类的_declared_fields,将当前类没有的字段添加到列表base_fields

  • 1.4 将父类字段与自己的字段合并列表返回

简单的将两个列表相加返回,最后调用type的__new__创建类

4. 实例化类

创建对象以及前置的加载字段都是在django项目运行时就执行的,而当请求到来时,视图函数从数据库获取数据并序列化,此时就来到了实例化类的过程

queryset = models.UserInfo.objects.all()
ser = UserSerializer(queryset, many=True)

instance = models.UserInfo.objects.all().first()
ser = UserSerializer(instance, many=False)

先讲解第二个,即单个对象的情况

实例化类依次执行__new____init__方法,两个方法均在BaseSerializer才有定义,如下

class BaseSerializer(Field):
    def __new__(cls, *args, **kwargs):
        if kwargs.pop('many', False):
            return cls.many_init(*args, **kwargs)
        # many=Flase则走这条逻辑,即只处理单对象,则调用object基类的__new__创建对象,紧接着寻找__init__方法
        return super().__new__(cls, *args, **kwargs)


    def __init__(self, instance=None, data=empty, **kwargs):
        self.instance = instance

返回的就是UserSerializer对象

如果是第一个,即多个对象的queryset的情况

class BaseSerializer(Field):
    def __new__(cls, *args, **kwargs):
        # many=True,处理多对象,进入if内部,执行many_init方法
        if kwargs.pop('many', False):
            return cls.many_init(*args, **kwargs)
        return super().__new__(cls, *args, **kwargs)

    def __init__(self, instance=None, data=empty, **kwargs):
        self.instance = instance

    @classmethod
    def many_init(cls, *args, **kwargs):
        child_serializer = cls(*args, **kwargs)  # 实例化UserSerializer对象
        list_kwargs = {
            'child': child_serializer,
        }
        meta = getattr(cls, 'Meta', None)
        list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)  # Meta中可以定义,默认为ListSerializer
        return list_serializer_class(*args, **list_kwargs)  # 实例化ListSerializer(),并将每一个UserSerializer对象传入

此时返回的就是不是UserSerializer对象了,而是ListSerializer对象

5. 序列化过程

上一节我们介绍的实例化类对应的是ser = UserSerializer(queryset, many=True)这一行代码,此时只是将数据封装到对象中,真正触发序列化动作的是当ser.data

5.1 UserSerializer类

我们先说当前类,即UserSerializer类触发序列化的情况

ser.data --> Serializer中的data --> BaseSerializer中的data --> self._data = self.to_representation(self.instance)

于是关键就是Serializer的to_representation方法,我们下面来分析:

  1. 获取所有的字段fields = Serializer._readable_fields
    1. self._readable_fields读取Serializer.fields的值
    2. Serializer.fields又从ModelSerializer.get_fields中拿值
    3. ModelSerializer.get_fields将所有字段(不管是自定义的还是模型类对象)转成序列化器对象,存入字典
  2. 循环所有的字段对象for field in fields:
    1. 去数据库对象中依据字段的信息获取数据attribute = field.get_attribute(instance)
      1. bind处理source,如果是外键跨表的形式如depart.title,就会被分割成一个列表存入self.source_attrs
      2. Field.get_attribute调用 get_attribute(instance, self.source_attrs)
      3. get_attribute循环source_attrs取出instance对应该source的值
    2. 将获取到的数据通过序列化器类的to_representation方法转变一下格式加入ret中,如IntegerField会对数据进行int()转换等等ret[field.field_name] = field.to_representation(attribute)

精简版的源码,结合上述文字流程食用

from django.contrib.gis.gdal.raster import source


class BaseSerializer(Field):
    @property
    def data(self):
        if not hasattr(self, '_data'):
            if self.instance is not None and not getattr(self, '_errors', None):
                # 真正实现序列化的步骤
                self._data = self.to_representation(self.instance)
            elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
                self._data = self.to_representation(self.validated_data)
            else:
                self._data = self.get_initial()
        return self._data


class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
    @property
    def data(self):
        ret = super().data
        return ReturnDict(ret, serializer=self)

    def to_representation(self, instance):
        ret = OrderedDict()
        # 1.获取所有的字段:先前加载的 InfoSerializer._declared_fields + Meta中指定的fields字段一一创建对象
        fields = self._readable_fields
        # 2. 循环所有的字段对象(都是序列化器字段对象)
        for field in fields:
            # 去数据库对象中依据字段的信息获取数据
            attribute = field.get_attribute(instance)
            # 将获取到的数据通过序列化器类的to_representation方法转换为适合 JSON 序列化的格式加入ret中,如IntegerField会对数据进行int()转换等等
            ret[field.field_name] = field.to_representation(attribute)
        return ret

    @cached_property
    def fields(self):
        fields = BindingDict(self)
        # 构建包含所有字段的fields字典
        for key, value in self.get_fields().items():
            fields[key] = value
        return fields

    @property
    def _readable_fields(self):
        for field in self.fields.values():
            yield field


class ModelSerializer(Serializer):
    def get_fields(self):
        # 获取在类变量中定义的那些字段,即前面提到的id,title,order
        declared_fields = copy.deepcopy(self._declared_fields)
        # 去 UserSerializer 中的 Meta 中找 model 字段,即 model = models.UserInfo
        model = getattr(self.Meta, 'model')
        # 返回一个包含模型字段和关系的详细信息的字典,包含了每个字段的详细信息,例如字段类型、是否是关系字段(如外键、多对多字段等)以及其他元数据
        info = model_meta.get_field_info(model)
        # 根据 declared_fields 和 info 来确定最终的字段名称列表。这个方法可能会根据序列化器的配置和模型的字段信息来排除或包含某些字段
        field_names = self.get_field_names(declared_fields, info)

        fields = OrderedDict()

        for field_name in field_names:
            # 根据字段的类型和属性来决定使用哪个序列化器字段类,以及如何配置这个字段
            # 简单来说就是把数据库字段转成序列化器字段类如models.CharField类转成CharField类,然后放入fields中
            field_class, field_kwargs = self.build_field(
                source, info, model, depth
            )
            # Create the serializer field.
            fields[field_name] = field_class(**field_kwargs)

        # Add in any hidden fields.
        fields.update(hidden_fields)

        return fields


class Field:
    def __init__(self, ...source):
        self.source = source

    def get_attribute(self, instance):
        # 此处的source_attrs是source经过bind处理后的
        # get_attribute就去instance中取出对应的值,如列表["depart","title"],会循环去取值,返回instance.depart.title
        return get_attribute(instance, self.source_attrs)

    def bind(self, field_name, parent):
        if self.source == '*':
            self.source_attrs = []
        else:
            # 如果是外键跨表的形式如depart.title,就会被分割成一个列表
            self.source_attrs = self.source.split('.')


def get_attribute(instance, attrs):
    for attr in attrs:
        try:
            if isinstance(instance, Mapping):
                instance = instance[attr]
            else:
                instance = getattr(instance, attr)
        except ObjectDoesNotExist:
            return None
        if is_simple_callable(instance):
            try:
                instance = instance()
            except (AttributeError, KeyError) as exc:
                # If we raised an Attribute or KeyError here it'd get treated
                # as an omitted field in `Field.get_attribute()`. Instead we
                # raise a ValueError to ensure the exception is not masked.
                raise ValueError(
                    'Exception raised in callable attribute "{}"; original exception was: {}'.format(attr, exc))

    return instance


class IntegerField(Field):
    def __init__(self, **kwargs):
        self.max_value = 111
        super().__init__(**kwargs)

    def to_representation(self, value):
        return int(value)


class InfoSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField()  # {max_value:111, _creation_counter:0,source="id"}source未定义默认为字段名
    title = serializers.CharField()  # {allow_blank:False, _creation_counter:1}
    order = serializers.IntegerField  # {max_value:111, _creation_counter:2}

    class Meta:
        model = models.Depart
        fields = "__all__"

5.2 ListSerializer类

如果已经了解了UserSerializer类的序列化过程,那么ListSerializer类的序列化只是在他的基础上多了一个循环

ser.data --> ListSerializer中的data --> BaseSerializer中的data --> self._data = self.to_representation(self.instance)

但是此时的to_representation优先找的是ListSerializer类中自己定义的的

我们先看一下ListSerializer类是如何实例化的

@classmethod
    def many_init(cls, *args, **kwargs):
        child_serializer = cls(*args, **kwargs)  # 实例化UserSerializer对象
        list_kwargs = {
            'child': child_serializer,
        }
        meta = getattr(cls, 'Meta', None)
        list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)  # Meta中可以定义,默认为ListSerializer
        return list_serializer_class(*args, **list_kwargs)  # 实例化ListSerializer(),并将每一个UserSerializer对象传入

可以看到,ListSerializer类中是一个个UserSerializer对象

因此ListSerializer类在to_representation方法时循环每一个UserSerializer对象并调用它的to_representation方法,显然,就是一遍又一遍的重复我们上一小节分析的UserSerializer类序列化的过程,此处便不再叙述

	    
class ListSerializer(BaseSerializer):
    def to_representation(self, data):
        iterable = data.all() if isinstance(data, models.Manager) else data

        return [
            self.child.to_representation(item) for item in iterable
        ]

6. 总结

至此,本文就已经结束了,我们从字段的创建到类的创建,类的实例化,再到数据的序列化,完整的分析了序列化器的序列化功能,相信此时再返回去看流程概述你会很清晰。从上一节序列化的使用到这一节源码分析,相信你已经掌握了序列化器的序列化作用,下一篇文章我们将进入到序列化器的数据校验的介绍,敬请期待!

标签:__,field,fields,self,instance,源码,序列化,DRF
From: https://blog.csdn.net/2404_89710001/article/details/144740608

相关文章

  • Java面试要点97 - Java中ThreadPoolExecutor源码解析
    文章目录引言一、核心属性1.1状态与线程数量的原子控制1.2任务队列与工作线程组二、Worker线程包装类2.1Worker类的设计三、任务提交源码分析3.1execute方法实现3.2addWorker核心方法四、任务执行源码分析4.1runWorker方法实现4.2getTask方法分析五、线程池......
  • SAAS版 云会计财务源码
     SAAS版云会计财务源码有许多优势:自动化:云会计财务源码具有自动化的特点,可以自动处理日常的财务事务,包括录入、分类、总账、报表等,减少了手工操作的繁琐和容易出错的问题。实时性:云会计财务源码可以实时更新财务数据,提供实时的财务报表和分析,使企业能够及时了解自己的财务......
  • 【最新原创毕设】基于PPH的花涧订购系统+00332(免费领源码)可做计算机毕业设计JAVA、PHP
    摘 要近年来,电子商务的快速发展引起了行业和学术界的高度关注。花涧订购系统旨在为用户提供一个简单、高效、便捷的花卉购物体验,它不仅要求用户清晰地查看所需信息,而且还要求界面设计精美,使得功能与页面完美融合,从而提升系统的可操作性。因此,我们需要深入研究信息内容,并利用......
  • 同城服务家政服务家政派单系统源码微信小程序+微信公众号+APP+H5
    JAVA同城服务家政服务家政派单系统源码:打造全方位家政服务生态在当今快节奏的社会中,家政服务已成为城市居民生活中不可或缺的一部分。为了满足广大用户对家政服务的多样化需求,我们精心打造了一款集微信小程序、微信公众号、APP及H5于一体的JAVA同城服务家政派单系统。该系统不......
  • 同城服务家政服务家政派单系统源码微信小程序+微信公众号+APP+H5
    JAVA同城服务家政服务家政派单系统源码:打造全方位家政服务生态在当今快节奏的社会中,家政服务已成为城市居民生活中不可或缺的一部分。为了满足广大用户对家政服务的多样化需求,我们精心打造了一款集微信小程序、微信公众号、APP及H5于一体的JAVA同城服务家政派单系统。该系统不......
  • PHP文字转链接API源码
    自己建一个data文件夹在同一个目录 文字换行符就是\n<源码><?phpif(isset($_GET["text"])){  $text=$_GET["text"];  if(!empty($text)){    //创建一个data文件夹    $folder="data";    if(!file_exists($folder)){......
  • 基于java的SpringBoot/SSM+Vue+uniapp的小型企业办公自动化系统的详细设计和实现(源码
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • AppAgent 源码 (xml 解析)
    1.数据准备adbshelluiautomatordump/sdcard/output.xml#获取手机ui界面的xml文件adbpull/sdcard/output.xmloutput.xml #将手机上的xml文件拉取到电脑上具体的xml文件:<?xmlversion='1.0'encoding='UTF-8'standalone='yes'?><hierarchyrotati......
  • Java 线程池深入剖析:核心概念、源码解析与实战应用
    线程池是现代多线程编程中的重要工具,它能显著提升任务处理效率并优化系统资源。本文将全面解析Java中的线程池机制,帮助开发者深入了解线程池的工作原理、实现方式及其最佳实践。一、基础概念1.什么是线程池?线程池是一种用于管理和复用线程资源的高效工具,能够在程序中......
  • 【汇总】Android源码文件说明
    一、文件名排序文件名英文说明中文说明备注、示例A    B    C    D    E     build/core/envsetup.mk 核心变量 F    G    H    I    G    ......