如何用单个DRF Serializer适配接口与drf-spectacular Schema?
解决DRF SerializerMethodField与drf-spectacular Schema生成兼容问题
问题背景
我在使用Django Rest Framework(DRF)和drf-spectacular时遇到问题:编写的GetConversionCasesSerializer能正常处理接口逻辑,但drf-spectacular的GUI无法正确生成对应的Schema。目前只能维护两个Serializer(业务用和Schema用),希望修改原Serializer实现二者合一。
原业务Serializer代码:
class GetConversionCasesSerializer(serializers.Serializer): conversionId = serializers.SerializerMethodField() cases = serializers.SerializerMethodField() def get_cases(self, obj): serializer = ResultDataSerializer(ResultData.objects.filter(conversion=obj), many=True) data = serializer.data return data def get_conversionId(self, obj): return obj.id
Schema专用Serializer代码:
class GetConversionCasesSerializerSchema(serializers.Serializer): conversionId = serializers.IntegerField() cases = serializers.ListSerializer(child=ResultDataSerializer())
接口代码:
@extend_schema(request=None, responses=GetConversionCasesSerializerSchema()) def get(self, *args, **kwargs): if self.request.version == "v1": conversion_id = self.kwargs.get('conversion_id') instance = Conversion.objects.get(id=conversion_id) serializer = GetConversionCasesSerializer(instance) return Response(serializer.data) else: return Response(status=status.HTTP_400_BAD_REQUEST)
解决方案
问题根源是drf-spectacular无法自动推断SerializerMethodField的返回数据结构,因为该字段的输出是动态生成的。只需给SerializerMethodField显式声明对应的Schema类型,即可让一个Serializer同时满足业务逻辑和Schema生成需求,有两种实现方式:
方式一:在SerializerMethodField中指定type参数
直接在定义SerializerMethodField时,通过type参数声明该字段对应的基础Serializer类型:
from rest_framework import serializers class GetConversionCasesSerializer(serializers.Serializer): # 显式声明conversionId为IntegerField类型 conversionId = serializers.SerializerMethodField(type=serializers.IntegerField()) # 显式声明cases为包含ResultDataSerializer的ListSerializer类型 cases = serializers.SerializerMethodField(type=serializers.ListSerializer(child=ResultDataSerializer())) def get_cases(self, obj): serializer = ResultDataSerializer(ResultData.objects.filter(conversion=obj), many=True) return serializer.data def get_conversionId(self, obj): return obj.id
方式二:使用extend_schema_field装饰器
通过drf-spectacular提供的extend_schema_field装饰器,修饰对应的get_*方法,声明返回值的Schema类型:
from drf_spectacular.utils import extend_schema_field from rest_framework import serializers class GetConversionCasesSerializer(serializers.Serializer): conversionId = serializers.SerializerMethodField() cases = serializers.SerializerMethodField() @extend_schema_field(serializers.IntegerField()) def get_conversionId(self, obj): return obj.id @extend_schema_field(serializers.ListSerializer(child=ResultDataSerializer())) def get_cases(self, obj): serializer = ResultDataSerializer(ResultData.objects.filter(conversion=obj), many=True) return serializer.data
更新接口代码
最后修改接口的@extend_schema装饰器,将responses参数改为原Serializer的实例:
@extend_schema(request=None, responses=GetConversionCasesSerializer()) def get(self, *args, **kwargs): if self.request.version == "v1": conversion_id = self.kwargs.get('conversion_id') instance = Conversion.objects.get(id=conversion_id) serializer = GetConversionCasesSerializer(instance) return Response(serializer.data) else: return Response(status=status.HTTP_400_BAD_REQUEST)
这样修改后,drf-spectacular就能正确识别字段的Schema结构,GUI展示正常,同时Serializer仍能处理原有业务逻辑,无需维护两个重复的Serializer。
内容的提问来源于stack exchange,提问作者LukaszK
相关产品推荐
相关产品推荐

