目录
Yo!Yo! 为什么说到感情呢?因为在restframe_work中,序列化与反序列化中有太多令人感到纠结,易乱,不好分清界限的地方。就像感情一样。
对于普通序列化类,作用的是普通python 对象,一般不需要反序列化得到一个对象,我们只需要得到一个反序列化后的数据字典,然后自己再来根据python普通对象的类进行实例化出对象。所以一般序列化类是用不到save和update还有create方法的。
然而, 对于特殊的序列化类,ModelSerializer/ListSerializer都会提供save/update/create三种方法,目的是直接通过使用反序列化得到的validated_data进行创建一个新对象或者更新一个已有对象。
如果提供了instance, 那么save会调用的时update方法,进行更新操作。这里的更新操作只会当前对象的,且支持全量更新和部分更新。因为时依据validated_data中有的属性进行更新。如果在序列化时提供了partail=True,那么在validated_data就是部分数据,就是部分更新。
save源代码,从源代码可以看出,无论序列化类目的是序列化还是反序列化,只要提供了instance,都会影响到save操作:
以下描述来自BaseSerializer源码注释
In particular,
if a `data=` argument is passed then:
.is_valid() - Available.
.initial_data - Available.
.validated_data - Only available after calling `is_valid()`
.errors - Only available after calling `is_valid()`
.data - Only available after calling `is_valid()`
If a `data=` argument is not passed then:
.is_valid() - Not available.
.initial_data - Not available.
.validated_data - Not available.
.errors - Not available.
.data - Available.
instance/data/many/partial 影响序列化对象行为的四个关键参数。
@property
def data(self):
if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'):
msg = (
'When a serializer is passed a `data` keyword argument you '
'must call `.is_valid()` before attempting to access the '
'serialized `.data` representation.\n'
'You should either call `.is_valid()` first, '
'or access `.initial_data` instead.'
)
raise AssertionError(msg)
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
小结: instance影响save行为;data是影响后续的所有行为,有data必须进行校验动作(.is_valid)。
前两者也是比较迷惑人的。都叫data,前者是Serializer(data={‘a‘:1,‘b‘:2}) ,后者是序列化后的数据。后者是前者依靠序列化定义的字段过滤校验后的。
而validated_data这是反序列化的目的对象的数据。
首先要知道,由于序列化主要对应的是查询动作,如list,retrieve两个。而destrory动作不涉及序列化类。update和partial_update,create动作则涉及反序列化。明确这些后,看下面:
所有的序列化类都继承自BaseSerializer类,这个类重写了__new__ 方法,会根据实例化时的参数many的布尔值。默认是False,则使用当前类作为单对象序列化的类。如果是True,那么__new__会返回一个ListSerializer类对象作为序列化对象,与此同时这个对象的一个child属性将会存放当前这个类的一个实例为后续提供多对象中单个对象的处理功能。
源码:
class CustomSerializer(serializers.Serializer):
...
class Meta:
list_serializer_class = CustomListSerializer # 使用自定义的序列化类
class BookListSerializer(serializers.ListSerializer):
def update(self, instance, validated_data):
# Maps for id->instance and id->data item.
book_mapping = {book.id: book for book in instance}
data_mapping = {item['id']: item for item in validated_data}
# Perform creations and updates.
ret = []
for book_id, data in data_mapping.items():
book = book_mapping.get(book_id, None)
if book is None:
ret.append(self.child.create(data))
else:
ret.append(self.child.update(book, data))
# Perform deletions.
for book_id, book in book_mapping.items():
if book_id not in data_mapping:
book.delete()
return ret
class BookSerializer(serializers.Serializer):
# We need to identify elements in the list using their primary key,
# so use a writable field here, rather than the default which would be read-only.
id = serializers.IntegerField()
...
class Meta:
list_serializer_class = BookListSerializer
多对象的反序列化只支持create操作,不支持更新update和部分更新partial_update,因为更新操作需要提供说要更新的目的对象,而要获得更新操作的目的对象,(一般是一个model对象)所以需要pk,然而我们的pk都是放在url中进行存放,通过lookup_url_kwarg来获取pk,不肯能将多个对象pk放入url中,所以更新操作是不支持批量反序列化。只有通过上面提到的自定义更新。
如果对象的属性还是一个对象,这时候的对象序列化就要用到子序列化了。
很多时候不知道怎么让serializer对象和 instance 匹配序列化上的,只有通过看源码了。里面封装了来达到序列化和反序列化的方法。如 以下源码截图,就是一步一步得到的.
也可以通过源码看出instance参数和data参数的关系
所有field类的父类,定义初始化时都是默认可读可写
最后,从source或者额外的method
最后就到了每个种Field类自己的序列化函数to_presentation()了,最后返回序列化结果
对于ModelSerializer 继承了Serializer, 但是其fields定义的字段的字典{‘fieldname‘: field_object} ,是通过重写了get_fields()方法。重写的方法逻辑如下截图:
或者说是,他们所展现出来的多态性,只不过各自的内部逻辑实现根据各自的特征不同而已。
只要大体了解,那么我们既可以对各种需要序列化的对象进行大致分类。虽然这些对象都千差万别,但是最终都有会根据其不同目的,实现了满足对应需求的序列化方式。这种设计模式值得思考和借鉴,能够将那么多的数据类型并且这么细化的分类实现,分类解耦,应对各种序列化需求,能够清晰做出选择,几乎不用重造轮子。
不同的获取数据方式,不同的序列化方式,可读写的选择,等等,为了满足需求,有太多的已实现的字段类可供选择。只要我们清楚以下几种类型他们实例化或者使用上的区别就可以了。
这里只看分类,具体参见 官档:
https://www.django-rest-framework.org/api-guide/relations/
int/string等。
ChoiceField/MultipleChoiceField
FileField/ImageField
ListField/DictField/HStoreField/JSONField
SerializerMethodField
StringRelatedField/PrimaryKeyRelatedField/HyperlinkedRelatedField
ReadOnlyField/HiddenField/ModelField
提供了Model字段与Serializer字段的映射,也提供对关系字段的映射,包括稍微复杂的GenericForeignKey关系。
class CourseModelSerializer(serializers.ModelSerializer):
level = serializers.CharField(source='get_level_display') # 利用source参数 在choice字段上
recommend_courses = serializers.SerializerMethodField() # manytomany 自定义序列化
chapters = serializers.SerializerMethodField()
image = serializers.SerializerMethodField()
class Meta:
model = Course
fields = ['id', 'title', 'image', 'level', 'recommend_courses', 'chapters']
def get_recommend_courses(self, obj):
qs = obj.re_course.all()
return [{'id': i.course.id, 're_course_title': i.course.title} for i in qs]
def get_chapters(self, obj):
if isinstance(self, ListSerializer):
return ''
qs = obj.chapters_set.all()
return [{'id': j.id, 'chapter_name': j.title} for j in qs]
def get_image(self, obj):
rel_path = obj.image
rely_on_path = settings.STATIC_URL
return rely_on_path + rel_path
# 主课程分类
class CourseCategoryModelSerializer(serializers.ModelSerializer):
subcategory_url = serializers.HyperlinkedRelatedField(source='coursesubcategory_set',
view_name='luffyapi:coursesubcategory-detail',
read_only=True, # 设置为只读
many=True) # 对于多对象
class Meta:
model = CourseCategory
fields = ['id', 'name', 'subcategory_url']
# 子课程分类
class CourseSubCategoryModelSerializer(serializers.ModelSerializer):
"""
子课程分类 序列化
"""
category_url = serializers.HyperlinkedRelatedField(source='category', view_name='luffyapi:coursecategory-detail',
lookup_field='pk',
lookup_url_kwarg='pk', read_only=True)
courses_url = serializers.HyperlinkedIdentityField(
view_name='luffyapi:coursesubcategory-get-courses',
read_only=True)
class Meta:
model = CourseSubCategory
fields = ['id', 'name', 'category', 'category_url', 'courses_url']
Rest_framework Serializer 序列化 (含源码浅解序列化过程)
原文:https://www.cnblogs.com/ZJiQi/p/10517865.html