Title(EN): Django REST Framework Learning Notes (9): the ListSerializer class in serialziers module  
Author: dog2 
 
 
基本信息 
源码:rest_framework.serializers  
官方文档
API Guide - Serializers  
API Guide - Serializer fields  
API Guide - 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 class  BookListSerializer (ListSerializer) :    def  update (self, instance, validated_data) :                              for  index, obj in  enumerate(instance):             self.child.update(obj, validated_data[index])         return  instance class  V3BookModelSerializer (ModelSerializer) :    class  Meta :                          list_serializer_class = BookListSerializer 
 
之所以需要实现update方法,群改时会走 rest_framework.serializers.ListSerializer中的update函数,而该函数并未被实现。
而群增不需要重写create方法,因为源码中rest_framework.serializers.ListSerializer走的就是ModelSerializer的create方法:
1 2 3 4 def  create (self, validated_data) :    return  [         self.child.create(attrs) for  attrs in  validated_data     ] 
 
视图层 views.py 
在视图层将 单局部改和群局部改整合,思路如下:
单局部改:对 v3/books/pk/ pk通过路由传参,修改数据选择传参,通过数据包json传递 
群局部修改:v3/books/ 修改数据通过数据包传递,设置成列表格式 [{pk:1,name:123},{pk:3,price:7},{pk:7,publish:2}] 
先将单改,群改的数据都格式化成 pks=[要需要的对象主键标识], request_data=[每个要修改的对象对应的修改数据] 
pks与request_data数据筛选,将pks中的没有对应数据的pk与数据已删除的pk移除,request_data对应索引位上的数据也移除,将合理的pks转换为objs 
 
 Source Code    
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 from  rest_framework.views import  APIViewfrom  rest_framework.response import  Responsefrom  . import  models, serializersclass  V3Book (APIView) :                   def  patch (self, request, *args, **kwargs) :         request_data = request.data           pk = kwargs.get('pk' )                  if  pk and  isinstance(request_data, dict):               pks = [pk, ]             request_data = [request_data, ]         elif  not  pk and  isinstance(request_data, list):               pks = []                          for  dic in  request_data:                 pk = dic.pop('pk' , None )                   if  pk:                     pks.append(pk)                                  else :                     return  Response({                         'status' : 1 ,                         'msg' : '参数错误'                      })         else :             return  Response({                 'status' : 1 ,                 'msg' : '参数错误'              })                                    objs = []         new_request_data = []         for  index, pk in  enumerate(pks):             try :                                  book_obj = models.Book.objects.get(pk=pk, is_delete=False )                 objs.append(book_obj)                                  new_request_data.append(request_data[index])             except :                                                                                     continue                   book_ser = serializers.V3BookModelSerializer(             instance=objs, data=new_request_data, partial=True , many=True )         book_ser.is_valid(raise_exception=True )         book_objs = book_ser.save()         return  Response({             'status' : 0 ,             'msg' : 'ok' ,             'results' : serializers.V3BookModelSerializer(book_objs, many=True ).data         }) 
 
 
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    class  MyListSerializer (ListSerializer) :    def  update (self, instance, validated_data) :                                    for  index, obj in  enumerate(instance):             self.child.update(obj, validated_data[index])         return  instance     class  MyModelSerializer (ModelSerializer) :         class  Meta :             list_serializer_class = MyListSerializer          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(序列化用)参数。