如何使用drf-spectacular文档化含多数据类型的元组?
DRF-Spectacular无法正确文档化Tuple类型的points字段
问题背景
使用drf-spectacular生成Swagger文档时,视图集直接返回原生字典(未经过DRF序列化器处理),字典结构如下:
data = { "lines": List[str], "points": List[Tuple[float, float, int, str]], "next_failure_date": str, "estimate_nth_failure": str, "status": str, }
但Swagger文档中points字段仅被识别为"points": ["string"],无法正确展示Tuple的内部结构。尝试自定义序列化器和字段后仍无效果:
class SwaggerGrowthChartPoint: """Class used to generate the Swagger documentation""" def __init__(self, x: float, y: float, sap_id: int, created_on: str): self.x = x self.y = y self.sap_id = sap_id self.created_on = created_on class SwaggerGrowthChartPointField(serializers.Field): def to_representation(self, value: SwaggerGrowthChartPoint): return f"{value.x}, {value.y}, {value.sap_id}, {value.created_on}" def to_internal_value(self, data): return SwaggerGrowthChartPoint(*data) class SwaggerGrowthChartSerializer(serializers.Serializer): """Serializer used to generate the Swagger documentation""" lines = SwaggerGrowthChartLinesSerializer(many=True) points = serializers.ListField(child=SwaggerGrowthChartPointField()) next_failure_date = serializers.ListField(child=serializers.DateField()) estimate_nth_failure = serializers.DateField() status = serializers.CharField()
解决方案
方案1:通过@extend_schema手动定义响应Schema
直接在视图集上用@extend_schema装饰器指定响应的OpenAPI结构,精准定义Tuple的每个元素类型:
from drf_spectacular.utils import extend_schema from rest_framework import viewsets, Response @extend_schema( responses={ 200: { "type": "object", "properties": { "lines": {"type": "array", "items": {"type": "string"}}, "points": { "type": "array", "items": { "type": "array", "items": [ {"type": "number", "format": "float"}, {"type": "number", "format": "float"}, {"type": "integer"}, {"type": "string"} ], "minItems": 4, "maxItems": 4 } }, "next_failure_date": {"type": "string", "format": "date"}, "estimate_nth_failure": {"type": "string", "format": "date"}, "status": {"type": "string"} } } } ) class GrowthChartViewSet(viewsets.ViewSet): def list(self, request): data = { "lines": ["line_a", "line_b"], "points": [(1.5, 2.7, 1001, "2024-05-01"), (3.2, 4.9, 1002, "2024-05-02")], "next_failure_date": "2024-11-30", "estimate_nth_failure": "2025-04-15", "status": "normal" } return Response(data)
方案2:调整序列化器并配合extend_schema
修改自定义字段,用@extend_schema_field明确告诉drf-spectacular字段的输出结构,再指定响应序列化器:
from rest_framework import serializers from drf_spectacular.utils import extend_schema_field, extend_schema from rest_framework import viewsets, Response class SwaggerGrowthChartPointField(serializers.Field): @extend_schema_field({ "type": "array", "items": [ {"type": "number", "format": "float"}, {"type": "number", "format": "float"}, {"type": "integer"}, {"type": "string"} ], "minItems": 4, "maxItems": 4 }) def to_representation(self, value): # 直接返回原Tuple结构,匹配视图返回的数据格式 return value def to_internal_value(self, data): return tuple(data) class SwaggerGrowthChartSerializer(serializers.Serializer): lines = serializers.ListField(child=serializers.CharField()) points = serializers.ListField(child=SwaggerGrowthChartPointField()) next_failure_date = serializers.DateField() estimate_nth_failure = serializers.DateField() status = serializers.CharField() @extend_schema(responses=SwaggerGrowthChartSerializer) class GrowthChartViewSet(viewsets.ViewSet): def list(self, request): data = { # 你的数据逻辑 } return Response(data)
方案3:使用TypedDict自动推断结构(Python 3.8+)
利用Python的TypedDict定义数据结构,让drf-spectacular自动识别并生成正确的Schema:
from typing import TypedDict, List, Tuple from drf_spectacular.utils import extend_schema from rest_framework import viewsets, Response class GrowthChartData(TypedDict): lines: List[str] points: List[Tuple[float, float, int, str]] next_failure_date: str estimate_nth_failure: str status: str @extend_schema(responses=GrowthChartData) class GrowthChartViewSet(viewsets.ViewSet): def list(self, request): data: GrowthChartData = { "lines": ["line1", "line2"], "points": [(0.8, 1.9, 2001, "2024-06-01")], "next_failure_date": "2024-12-01", "estimate_nth_failure": "2025-03-10", "status": "warning" } return Response(data)
内容的提问来源于stack exchange,提问作者Benjamin_Mourgues
相关产品推荐
相关产品推荐

