创建Django Rest Framework的方法序列化视图 Django Rest Framework(DRF)提供的框架极大的方便了REST API的开发工作。其中的generics模块提供了很多通用场景下的视图。但是在使用的过程中发现在模型序列化ModelSerializer方面总有些不便。
例如下面的情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import  uuidfrom  django.db import  modelsclass  Organization (models.Model):    org_uuid = models.UUIDField(         primary_key=True , default=uuid.uuid4, editable=False )     name = models.CharField(max_length=40 ) class  User (AbstractBaseUser ):    user_uuid = models.UUIDField(         primary_key=True , default=uuid.uuid4, editable=False )          org = models.ForeignKey('Organization' , on_delete=models.CASCADE,) 
通常在Model中都会遇到外键或者其它关系的字段。在创建User(无须连级创建Organization)或者更新其org字段时我们希望提供其所属的Organization主键org_uuid即可,而在获取一个User时,仅仅返回一个org_uuid并不能告诉调用者任何有意义的Organization信息。因此用于创建和获取User的序列化类也不尽相同。
事实上我更倾向于从请求的方法Method角度来决定使用什么样的序列化类。根据上面的情况,我通常会至少创建两种模型序列化类ModelSerializer:
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 from  rest_framework import  serializersfrom  myapp import  modelsclass  OrganizationListViewSerializer (serializers.ModelSerializer):    class  Meta :         model = models.Organization         fields = '__all__'  class  UserListViewSerializer (serializers.ModelSerializer):    org = OrganizationListViewSerializer()     class  Meta :         model = models.User         fields = '__all__'  class  UserCreateUpdateSerializer (serializers.ModelSerializer):    org = serializers.PrimaryKeyRelatedField(         queryset=models.Organization.objects.all ())     class  Meta :         model = models.User         fields = '__all__  
利用嵌套的序列化类UserListViewSerializer能自动扩展Organization的信息。而在修改或者创建User时,只需要提供Organization的主键即可。
现在回到generics模块提供的通用视图上,根据REST的原则,通常使用的是generics.ListCreateAPIView和generics.RetrieveUpdateAPIView, 这也是根据接口和请求方法区分出的通用视图,在视图这点上框架做得很好。但是针对序列化类还不够灵活,默认一个APIView只能定义一个serializer_class。而我需要的是根据不同请求方法区分出使用不同的序列化类。实现如下:
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 from  rest_framework import  exceptionsfrom  rest_framework import  genericsfrom  myapp import  modelsfrom  myapp import  serializers as  serclass  MethodSerializerView (object ):    '''      Utility class for get different serializer class by method.     For example:     method_serializer_classes = {         ('GET'): MyModelListViewSerializer,         ('PUT', 'PATCH'): MyModelCreateUpdateSerializer     }     '''     method_serializer_classes = None      def  get_serializer_class (self ):         assert  self.method_serializer_classes is  not  None , (             'Expected view %s should contain method_serializer_classes '              'to get right serializer class.'  %             (self.__class__.__name__, )         )         for  methods, serializer_cls in  self.method_serializer_classes.items():             if  self.request.method in  methods:                 return  serializer_cls         raise  exceptions.MethodNotAllowed(self.request.method) class  UsersListCreateView (MethodSerializerView, generics.ListCreateAPIView):    '''      API: /users     Method: GET/POST     '''     queryset = models.User.objects.all ()     method_serializer_classes = {         ('GET' ): ser.UserListViewSerializer,         ('POST' ): ser.UserCreateUpdateSerializer     } class  UsersDetailView (MethodSerializerView, generics.RetrieveUpdateAPIView):    '''      API: /user/:user_uuid     Method: GET/PUT/PATCH     '''     queryset = models.User.objects.all ()     method_serializer_classes = {         ('GET' ): ser.UserListViewSerializer,         ('PUT' , 'PATCH' ): ser.UserCreateUpdateSerializer     } 
注意最后两个View的类继承顺序,MethodSerializerView要在前,才能使得它的get_serializer_class被调用。
如此一来可以根据不同的方法采取不同序列化策略,😉