创建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
被调用。
如此一来可以根据不同的方法采取不同序列化策略,😉