Django REST Framework 学习笔记(九):序列化模块 serialziers 之 ListSerializer类

  • Title(EN): Django REST Framework Learning Notes (8): the ModelSerializer class in serialziers module
  • Author: dog2

基本信息

  • 源码:rest_framework.serializers
  • 官方文档
    • API Guild - Serializers
    • API Guild - Serializer fields
    • API Guild - Serializer relations
  • 本文demo代码Github

DRF常用序列化类主要有

  • rest_framework.serialziers.Serializer
  • rest_framework.serialziers.ModelSerializer
  • rest_framework.serialziers.ListSerializer

本篇介绍rest_framework.serialziers.ListSerializer,将继续以图书管理系统实例说明。

使用场景之群改群增群改

当一个序列化器在带有many=True选项被序列化时,将创建一个ListSerializer实例,该序列化器类将成为ListSerializer类的子类。 当你需要自定义多个对象的行为时(比如群增,群改),你需要手动定制ListSerializer类的一些行为。 可以通过在自定义序列化器的Meta类下面的list_serializer_class来绑定你需要的的ListSerializer

示例代码

群改需要设置ListSerializer,创建BookListSerializer继承ListSerializer,重写update方法

序列化层 serializer.py

1
2
3
4
5
6
7
8
9
10
11
12
13
# 重点:ListSerializer与ModelSerializer建立关联的是: 在ModelSerializer的Meta类中设置   list_serializer_class
class BookListSerializer(ListSerializer):
def update(self, instance, validated_data): # print(instance) # 要更新的对象们
# print(validated_data) # 更新的对象对应的数据们
# print(self.child) # 服务的模型序列化类 - V3BookModelSerializer
for index, obj in enumerate(instance):
self.child.update(obj, validated_data[index])
return instance

class V3BookModelSerializer(ModelSerializer):
class Meta:
# 群改,list_serializer_class是固定的key写法,直接转入BookListSerializer类的 update 方法
list_serializer_class = BookListSerializer

之所以需要实现update方法,群改时会走 rest_framework.serializers.ListSerializer中的update函数,而该函数并未被实现。

而群增不需要重写create方法,因为源码中rest_framework.serializers.ListSerializer走的就是ModelSerializercreate方法:

1
2
3
4
def create(self, validated_data):
return [
self.child.create(attrs) for attrs in validated_data
]

视图层 views.py

在视图层将 单局部改和群局部改整合,思路如下:

  1. 单局部改:对 v3/books/pk/ pk通过路由传参,修改数据选择传参,通过数据包json传递
  2. 群局部修改:v3/books/ 修改数据通过数据包传递,设置成列表格式 [{pk:1,name:123},{pk:3,price:7},{pk:7,publish:2}]
  3. 先将单改,群改的数据都格式化成 pks=[要需要的对象主键标识]request_data=[每个要修改的对象对应的修改数据]
  4. pksrequest_data数据筛选,将pks中的没有对应数据的pk与数据已删除的pk移除,request_data对应索引位上的数据也移除,将合理的pks转换为objs

Source Code

1
2


ListSerializer使用要点小结

views.py

1
ser_obj = ModelSerializer(instance=model_obj,data=model_data,partial=True,many=True)

serializer.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 实现群改功能,反序列化情况下的create、update就不再调用ModelSerializer的
# 而是调用 ModelSerializer.Meta.list_serializer_class 指向的 ListSerializer 类的create、update
# ListSerializer默认只实现了群增的create,要实现群改,必须重写update

  #自定义序列化类,重写update方法
class MyListSerializer(ListSerializer):
def update(self, instance, validated_data):
# print(instance) # 要更新的对象们: [obj1, obj2, ...]
# print(validated_data) # 更新的对象对应的数据们: [{}, {}, ...]
# print(self.child) # 服务的模型序列化类 - V2BookModelSerializer
for index, obj in enumerate(instance):
self.child.update(obj, validated_data[index])
return instance

class MyModelSerializer(ModelSerializer):
class Meta:
list_serializer_class = MyListSerializer

# 将两者类建立关联,在MyListSerializer中就可以用self.child拿到# MyModelSerializer,进而使用MyModelSerializer中封装好的方法
cls.Meta.list_serializer_class.child = cls

源码补充分析

分析一下修改为什么要用instance传参。

修改之后数据使用save()保存,从视图的save()点击进去查看源码,下面是BaseSerializer类中的save,而该save未被实现。

因此接着查看save方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

def save(self, **kwargs):
# ...

validated_data = [
dict(list(attrs.items()) + list(kwargs.items()))
for attrs in self.validated_data
]

if self.instance is not None:
self.instance = self.update(self.instance, validated_data)
assert self.instance is not None, (
'`update()` did not return an object instance.'
)
else:
self.instance = self.create(validated_data)
assert self.instance is not None, (
'`create()` did not return an object instance.'
)

return self.instance

instance存在就走update方法,修改数据(PUT/PATCH)时都应传入instance,所以单改群改都需要传入data(反序列化用)和instance(序列化用)参数。