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