目录
前言
序列化器是Django框架中的一个重要概念,用于在Python对象和JSON等格式之间进行相互转换。通过序列化器,我们可以方便地将模型实例转换为JSON格式的数据,并且还可以对数据进行验证、创建和更新等操作。
上一篇文章我们讲解了序列化器对数据的序列化功能
点此快速跳转-DRF之序列化器【1】序列化
温馨提示:序列化器源码中涉及元类的知识,如果对其不了解,建议翻看往期有关元类的介绍
点此快速跳转-python中的元类
本文我们从源码的角度分析序列化器对数据的序列化实现
1. 流程概述
此处只做粗略的介绍,看不懂没关系,建议看完后面的源码解析后再回来梳理一遍大致流程
第一步:加载字段,创建类
-
class BaseSerializer(serializers.Serializer): xx = serializers.CharField(source="name")
-
- 在类成员中提取出xx字段并删除
-
- 汇总到
BaseSerializer._declared_fields = {"xx":对象}
- 汇总到
-
class UserSerializer(serializers.ModelSerializer, BaseSerializer): yy = serializers.CharField(source="name") class Meta: model = models.UserInfo fields = ['name', 'age', "yy" ,"xx"]
-
- 在类成员中提取出yy字段并删除
- 将自己即继承类中的字段都汇总到
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=1
,Field._creation_counter = 2
order = serializers.IntegerField
同理,_creation_counter=2
,Field._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
,如果obj
是Field
的子类,则构建元组加入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方法,我们下面来分析:
- 获取所有的字段fields = Serializer._readable_fields
- self._readable_fields读取Serializer.fields的值
- Serializer.fields又从ModelSerializer.get_fields中拿值
- ModelSerializer.get_fields将所有字段(不管是自定义的还是模型类对象)转成序列化器对象,存入字典
- 循环所有的字段对象for field in fields:
- 去数据库对象中依据字段的信息获取数据attribute = field.get_attribute(instance)
- bind处理source,如果是外键跨表的形式如depart.title,就会被分割成一个列表存入self.source_attrs
- Field.get_attribute调用 get_attribute(instance, self.source_attrs)
- get_attribute循环source_attrs取出instance对应该source的值
- 将获取到的数据通过序列化器类的to_representation方法转变一下格式加入ret中,如IntegerField会对数据进行int()转换等等ret[field.field_name] = field.to_representation(attribute)
- 去数据库对象中依据字段的信息获取数据attribute = field.get_attribute(instance)
精简版的源码,结合上述文字流程食用
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