Title(EN): Django REST Framework Learning Notes (8): the ModelSerializer 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.ModelSerializer ,将结合图书管理系统实例说明。
 
序列化以外的其他配置 
settings.py 
1 2 3 4 5 6 7 8 9 10 11 12 13 INSTALLED_APPS = [          'rest_framework' , ] LANGUAGE_CODE = 'zh-hans'  TIME_ZONE = 'Asia/Shanghai'  USE_I18N = True  USE_L10N = True  USE_TZ = False  MEDIA_URL = '/media/'  MEDIA_ROOT = os.path.join(BASE_DIR, 'media' ) 
 
全局url.py 
1 2 3 4 5 6 7 8 9 from  django.contrib import  adminfrom  django.urls import  path, includefrom  django.conf import  settingsfrom  django.conf.urls.static import  staticurlpatterns = [     path('admin/' , admin.site.urls),     path('api/' , include('api.urls' )), ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 
 
数据库表 
表一览 
BaseModel基表:is_delete、create_time 
 
下面四表继承基表,可以继承两个字段
Book表:name、price、img、authors、publish、is_delete、create_time 
Publish表:name、address、is_delete、create_time 
Author表:name、age、is_delete、create_time 
AuthorDetail表:mobile, author、is_delete、create_time 
 
基表代码 
models.py
1 2 3 4 5 6 7 class  BaseModel (models.Model) :    is_delete = models.BooleanField(default=False )     create_time = models.DateTimeField(auto_now_add=True )          class  Meta :         abstract = True  
 
断关联多表关系(重点) 
外键位置: 
 
一对多:外键放多的一方 
一对一:从逻辑正反向考虑,如作者表与作者详情表,作者删除级联作者详情也删除,详情删除作者依旧存在,所以建议外键在详情表中(和之前的一对一关系外键放置不一样,之前都是把外键放在查询频率高的表) 
多对多:外键在关系表中 
 
ORM正向方向连表查找:(注:依赖代码见下方)
正向:通过外键字段 eg: author_detial_obj.author,外键设置在作者详情表,在作者详情表中查询作者直接.author就可以 
反向:通过设置反向查询related_name的值 eg:author_obj.detail,外键没有设置在作者表中,在作者表中通过设置反向查询.detail查询作者详情 
  
连表操作关系:(注:拿作者与作者详情表举例,外键建在作者详情表中)
作者删除,详情级联 1 on_delete=models.CASCADE     
 
作者删除,详情置空 1 null=True , on_delete=models.SET_NULL    
 
作者删除,详情重置 1 default=0 , on_delete=models.SET_DEFAULT 
 
作者删除,详情不动 1 on_delete=models.DO_NOTHING 
 
  
外键关联字段的参数:如何实现断关联、目前表间操作关系、方向查询字段
作者详情表中的(外键设置在作者详情表中) 1 2 3 4 5 6 author = models.OneToOneField(     to='Author' ,            related_name='detail' ,        db_constraint=False ,          on_delete=models.CASCADE   ) 
 
图书表中的 1 2 3 4 5 6 7 8 9 10 11 publish = models.ForeignKey(     to='Publish' ,     related_name='books' ,     db_constraint=False ,     on_delete=models.DO_NOTHING, ) authors = models.ManyToManyField(     to='Author'      related_name='books' ,     db_constraint=False , ) 
 
  
 
注意:
ManyToManyField不能设置on_delete 
OneToOneField、ForeignKey必须设置on_delete(django1.x系统默认级联,但是django2.x必须手动明确) 
 
 
各表代码 
 models.py  
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 from  django.db import  modelsclass  BaseModel (models.Model) :    is_delete = models.BooleanField(default=False )       create_time = models.DateTimeField(auto_now_add=True )          class  Meta :         abstract = True    class  Book (BaseModel) :    name = models.CharField(max_length=64 )     price = models.DecimalField(max_digits=5 , decimal_places=2 )     img = models.ImageField(upload_to='img' , default='default.jpg' )          publish = models.ForeignKey(         to='Publish' ,           db_constraint=False ,           related_name='books' ,           on_delete=models.DO_NOTHING,       )          authors = models.ManyToManyField(         to='Author' ,         db_constraint=False ,           related_name='books' ,       )          @property     def  publish_name (self) :            return  self.publish.name     @property     def  author_list (self) :         return  self.authors.values('name' , 'age' , 'detail__mobile' ).all()     class  Meta :         db_table = 'book'          verbose_name = '书籍'          verbose_name_plural = verbose_name         unique_together = ('name' , 'publish' )     def  __str__ (self) :         return  self.name class  Publish (BaseModel) :    """name、address、is_delete、create_time"""      name = models.CharField(max_length=64 )     address = models.CharField(max_length=64 )     class  Meta :         db_table = 'publish'          verbose_name = '出版社'          verbose_name_plural = verbose_name     def  __str__ (self) :         return  self.name class  Author (BaseModel) :    """name、age、is_delete、create_time"""      name = models.CharField(max_length=64 )     age = models.IntegerField()     class  Meta :         db_table = 'author'          verbose_name = '作者'          verbose_name_plural = verbose_name     def  __str__ (self) :         return  self.name class  AuthorDetail (BaseModel) :    """mobile, author、is_delete、create_time"""      mobile = models.CharField(max_length=11 )     author = models.OneToOneField(         to='Author' ,         db_constraint=False ,         related_name='detail' ,         on_delete=models.CASCADE     )     class  Meta :         db_table = 'author_detail'          verbose_name = '作者详情'          verbose_name_plural = verbose_name     def  __str__ (self) :         return  self.author.name 
 
 
serializers.ModelSerializer 
序列化 & 反序列化 
序列化 
ModelSerializer模型类序列化器与常规的Serializer相同,但提供了:
基于模型类自动生成一系列字段 
包含默认的create()和update()的实现 
基于模型类自动为Serializer生成validators,比如unique_together 
 
序列化层 api/serializers.py 
 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 from  rest_framework.serializers import  ModelSerializer, SerializerMethodFieldfrom  rest_framework.exceptions import  ValidationErrorfrom  . import  modelsclass  PublishModelSerializer (ModelSerializer) :    class  Meta :         model = models.Publish         fields = ('name' , 'address' ) class  BookModelSerializer (ModelSerializer) :                             publish = PublishModelSerializer()     class  Meta :                  model = models.Book                  fields = ('name' , 'price' , 'img' , 'author_list' , 'publish' )                                                                
 
 
视图层 views.py 
 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 from  rest_framework.views import  APIViewfrom  rest_framework.response import  Responsefrom  . import  models,serializersclass  Book (APIView) :    def  get (self, request, *args, **kwargs) :         pk = kwargs.get('pk' )                  if  pk:             try :                 book_obj = models.Book.objects.get(pk=pk, is_delete=False )                 book_data = serializers.BookModelSerializer(                     book_obj).data               except  Exception as  err:                 return  Response({                     'status' : 1 ,                     'msg' : '数据不存在'                  })                  else :             book_query = models.Book.objects.filter(                 is_delete=False ).all()               book_data = serializers.BookModelSerializer(                 book_query, many=True ).data                    return  Response({             'status' : 0 ,             'msg' : 'ok' ,             'results' : book_data         }) 
 
 
子路由层 api/urls.py 
1 2 3 4 urlpatterns = [     path('v1/books/' , views.Book.as_view()),     path('v1/books/<pk>/' , views.Book.as_view()), ] 
 
反序列化 
序列化层 api/serializers.py 
 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 class  BookModelDeserializer (ModelSerializer) :              re_name = CharField(read_only=True )     class  Meta :         model = models.Book         exclude = ['is_delete' , 'create_time' , 'img' ]                           extra_kwargs = {             'name' : {                 'required' : True ,                   'min_length' : 1 ,                 'error_messages' : {                     'required' : '必填项' ,                     'min_length' : '太短' ,                 },             },         }          def  validate_name (self, value) :                     if  'g'  in  value.lower():             raise  ValidationError('该g书不能出版' )         return  value          def  validate (self, attrs) :         publish = attrs.get('publish' )           name = attrs.get('name' )         if  models.Book.objects.filter(name=name, publish=publish):             raise  ValidationError({'book' : '该书已存在' })         return  attrs      
 
 
视图层 views.py 
1 2 3 4 5 6 7 8 9 10 11 12 class  Book (APIView) :    def  post (self, request, *args, **kwargs) :         request_data = request.data         book_ser = serializers.BookModelDeserializer(data=request_data)                  book_ser.is_valid(raise_exception=True )         book_obj = book_ser.save()         return  Response({             'status' : 0 ,             'msg' : 'ok' ,             'results' : serializers.BookModelSerializer(book_obj).data         }) 
 
子路由层 api/urls.py 
1 2 3 4 urlpatterns = [     path('v1/books/' , views.Book.as_view()),     path('v1/books/<pk>/' , views.Book.as_view()), ] 
 
注意点 
反序列化层:
在设置fields,没有默认值的字段都必须设置反序列化保存到数据库中 
使用extra_kwargs 来设置系统校验规则 
设置局部钩子和全局钩子 
ModelSerializer类已经帮我们实现了 create 与 update 方法,不需要再写 
  
视图层:
POST请求通过request.data拿到数据包 
传给反序列化 ,通过data=request_data传入需要反序列化的数据 
is_valid判断校验是否合格 ,raise_exception=True必须要写的 
通过.save()保存到数据库中 
  
 
 
序列化与反序列化整合 
序列化层 api/serializers.py 
 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 class  V2BookModelSerializer (ModelSerializer) :         re_name = CharField(read_only=True )     class  Meta :         model = models.Book         fields = ('name' , 'price' , 'img' , 'author_list' ,                   'publish_name' , 'publish' , 'authors' , 're_name' )         extra_kwargs = {             'name' : {                 'required' : True ,                 'min_length' : 1 ,                 'error_messages' : {                     'required' : '必填项' ,                     'min_length' : '太短' ,                 }             },             'publish' : {                 'write_only' : True              },             'authors' : {                 'write_only' : True              },             'img' : {                 'read_only' : True ,             },             'author_list' : {                 'read_only' : True ,             },             'publish_name' : {                 'read_only' : True ,             }         }     def  validate_name (self, value) :                  if  'g'  in  value.lower():             raise  ValidationError('该g书不能出版' )         return  value     def  validate (self, attrs) :         publish = attrs.get('publish' )         name = attrs.get('name' )                                                      return  attrs 
 
 
注意点 
fields中设置所有序列化与反序列化字段 
extra_kwargs划分只序列化或只反序列化字段(一般我们把需要存入到数据库中的使用write_only(反序列化),只需要展示的就read_only(序列化),看需求设计)
write_only:只反序列化 
read_only:只序列化 
自定义字段默认只序列化(read_only) 
如果字段没设置write_only或者read_only,那么该字段可以序列化和反序列化 
  
设置反序列化所需的 系统、局部钩子、全局钩子 等校验规则 
 
 
视图层 views.py 
 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 64 65 66 67 68 class  V2Book (APIView) :    def  get (self, request, *args, **kwargs) :         pk = kwargs.get('pk' )                  if  pk:             try :                 book_obj = models.Book.objects.get(pk=pk, is_delete=False )                 book_data = serializers.V2BookModelSerializer(                     book_obj).data               except :                 return  Response({                     'status' : 1 ,                     'msg' : '参数有误'                  })                  else :             book_query = models.Book.objects.filter(is_delete=False ).all()             book_data = serializers.V2BookModelSerializer(                 book_query, many=True ).data           return  Response({             'status' : 0 ,             'msg' : 'ok' ,             'results' : book_data         })     def  post (self, request, *args, **kwargs) :                           request_data = request.data         if  isinstance(request_data, dict):               many = False          elif  isinstance(request_data, list):               many = True          else :             return  Response({                 'status' : 1 ,                 'msg' : '数据错误'              })         book_ser = serializers.V2BookModelSerializer(             data=request_data, many=many)           book_ser.is_valid(raise_exception=True )                  book_result = book_ser.save()         return  Response({             'status' : 0 ,             'msg' : 'ok' ,             'results' : serializers.V2BookModelSerializer(book_result, many=many).data         })               def  delete (self, request, *args, **kwargs) :         pk = kwargs.get('pk' )         if  pk:             pks = [pk]         else :             pks = request.data.get('pks' )         if  models.Book.objects.filter(pk__in=pks, is_delete=False ).update(is_delete=True ):             return  Response({                 'status' : 0 ,                 'msg' : '删除成功'              })         return  Response({             'status' : 1 ,             'msg' : '删除失败'          }) 
 
 
注意点 
序列化数据最后必须要.data (因为要传给前端) 
反序列化通过data传参,序列化通过instance传参(当你只传一个参数时,默认是instance的参数) 
反序列化与序列化都能使用many=True,序列化和反序列化数据只要被[]嵌套都要写many=True 
 
 
子路由层 api/urls.py 
1 2 3 4 urlpatterns = [     path('v2/books/' , views.V2Book.as_view()),     path('v2/books/<pk>/' , views.V2Book.as_view()), ] 
 
使用场景总结 
序列化类初始化时,instance/data/many/partial 是影响序列化对象行为的四个关键参数。
序列化角度 
序列化 
如果没有data参数,只有instance,那么就不存在反序列化校验一说,只有序列化对象instance。
1 2 ser_obj = ModelSerializer(model_obj)    
 
反序列化 
如果有data,没有instance,则需要调用is_valid方法校验data,然后将data进行反序列化,得到validated_data,此时再通过序列化对象获取data,这个data和初始化提供的data不同,是序列化validated_data后的data,比起初始化data,可能减少了无效的字段(序列化没有定义的字段)。
1 2 ser_obj = ModelSerializer(data=model_data)   
 
数据操作角度(增删改查) 
修改场景 
如果同时提供了instance及data, 那么只要有data或者部分data,data都要进行验证才能进行下面的save等操作,如果不经过is_valid过程,那么后面的获取序列化数据或者反序列化数据都会无效。 
要用instance指明要修改的对象partial用于部分更新,之所以要伴随instance,是因为要指明给save用,在save操作时给那个instance部分更新。逻辑这回走到下面源码中的get_initial()获取要进行更新instance的字段数据。 
 
单改 
单整体改,说明前台要提供修改的数据,修改之后保存的数据需要校验,校验的数据应该在实例化“序列化类对象”时,赋值给data 
修改,就必须明确被修改的模型类对象,并在实例化“序列化类对象”时,赋值给instance,必须赋值给instance 
整体修改,所有校验规则有required=True的字段,都必须提供,因为在实例化“序列化类对象”时,参数partial默认为False 
 
整体改(PUT) 
1 2 3 4 5 V2BookModelSerializer(     instance=model_obj,      data=model_data,      partial=False ,  ) 
 
局部改(PATCH) 
1 2 3 4 5 6 V2BookModelSerializer(     instance=model_obj,      data=model_data,      partial=True ,  )      
 
群改 ListSerializer 
many参数将直接影响序列化类的类型,如果是many=False,那么直接使用当前序列化类。如果many=True,将实例化一个ListSerializer类来序列化或者反序列化类。
在前面的代码中,我们在V2BookModelSerializer类中并没有做对多对象序列化的特殊处理,但V2版本的api仍然支持群增和群删操作。究其原因,是由于BaseSerializer.__new__方法)或者class Meta:中定义了list_serializer_class指定的多对象序列化类。
关于 ListSerializer的详细介绍请看下一篇。
给序列化类传入自定义数据 
视图类views.py使用context传递参数给序列化类serializers.py
方法 
在视图类中实例化序列化对象时,可以设置context内容 
在序列化类中的局部钩子、全局钩子、create、update方法中,都可以用self.context访问视图类传递过来的内容。 
 
使用场景 
例如有类似如下需求时:
在视图类views中,可以通过request得到登陆用户request.user 
在序列化类中,要完成数据库数据的校验与入库操作,可能会需要知道当前的登陆用户,但序列化类默认无法访问request 
 
因此在视图类中实例化序列化对象时,将request对象传递进去
示例代码 
视图层 views.py 
1 2 3 4 5 6 7 8 9 10 class  Book (APIView) :    def  post (self, request, *args, **kwargs) :         book_ser = serializers.BookModelSerializer(data=request_data,context={'request' :request})            book_ser.is_valid(raise_exception=True )         book_result = book_ser.save()         return  Response({             'status' : 0 ,             'msg' : 'ok' ,             'results' : serializers.BookModelSerializer(book_result).data         }) 
 
序列化层 serializers.py 
1 2 3 4 5 6 7 class  BookModelSerializer (ModelSerializer) :    class  Meta :         model = models.Book         fields = ('name' , 'price' )     def  validate_name (self, value) :         print(self.context.get('request' ).method)           return  value 
 
扩展阅读 
django rest framework serializers小结