drf-standardized-errors无法为RetrieveAPIView生成404错误响应
根据drf-standardized-errors的文档,它可与DRF-Spectacular集成并生成错误响应文档,还提供了隐藏通用错误响应(如404、401)的方法。但我在使用DRF标准视图RetrieveAPIView时,仅能生成200响应,无法生成404错误响应文档。查看drf-standardized-errors的AutoSchema代码发现,它调用的drf-spectacular AutoSchema的get_response_serializers方法中并不包含404响应码。若在视图中手动抛出raise NotFound(detail="Object not found"),则能正常生成404响应文档。我是否误解了该自动集成的用途?
相关代码及配置
视图代码
class MemberDetailsView(generics.RetrieveAPIView): serializer_class = MemberDetailsSerializer schema = AutoSchema() def get_queryset(self): member_id = self.kwargs["pk"] cache_key = f"member_{member_id}" cached_member = cache.get(cache_key) if cached_member is None: print("NO CACHE") cached_member = TE_MemberList.objects.filter(id=self.kwargs["pk"]) cache.set(cache_key, cached_member, timeout=600) return cached_member
Settings配置
INSTALLED_APPS = [ ... "drf_standardized_errors", "drf_spectacular", ... ] REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": [ "rest_framework.authentication.TokenAuthentication", ], "DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"], "DEFAULT_THROTTLE_CLASSES": [ "rest_framework.throttling.ScopedRateThrottle", ], "DEFAULT_SCHEMA_CLASS": "drf_standardized_errors.openapi.AutoSchema", "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination", "PAGE_SIZE": 20, } DRF_STANDARDIZED_ERRORS = { "ENABLE_IN_DEBUG_FOR_UNHANDLED_EXCEPTIONS": True } SPECTACULAR_SETTINGS = { "TITLE": "Essential Classics App API", "DESCRIPTION": "YASA", "VERSION": "1.0.0", "SERVE_INCLUDE_SCHEMA": False, # OTHER SETTINGS "SCHEMA_PATH_PREFIX": "/api/v1/", "SERVE_PERMISSIONS": ["rest_framework.permissions.IsAdminUser"], "SERVE_AUTHENTICATION": ["rest_framework.authentication.BasicAuthentication"], "ENUM_NAME_OVERRIDES": { "ValidationErrorEnum": "drf_standardized_errors.openapi_serializers.ValidationErrorEnum.choices", "ClientErrorEnum": "drf_standardized_errors.openapi_serializers.ClientErrorEnum.choices", "ServerErrorEnum": "drf_standardized_errors.openapi_serializers.ServerErrorEnum.choices", "ErrorCode401Enum": "drf_standardized_errors.openapi_serializers.ErrorCode401Enum.choices", "ErrorCode403Enum": "drf_standardized_errors.openapi_serializers.ErrorCode403Enum.choices", "ErrorCode404Enum": "drf_standardized_errors.openapi_serializers.ErrorCode404Enum.choices", "ErrorCode405Enum": "drf_standardized_errors.openapi_serializers.ErrorCode405Enum.choices", "ErrorCode406Enum": "drf_standardized_errors.openapi_serializers.ErrorCode406Enum.choices", "ErrorCode415Enum": "drf_standardized_errors.openapi_serializers.ErrorCode415Enum.choices", "ErrorCode429Enum": "drf_standardized_errors.openapi_serializers.ErrorCode429Enum.choices", "ErrorCode500Enum": "drf_standardized_errors.openapi_serializers.ErrorCode500Enum.choices", }, "POSTPROCESSING_HOOKS": [ "drf_standardized_errors.openapi_hooks.postprocess_schema_enums" ], }
解答
你并没有误解集成的用途,问题出在get_queryset的实现逻辑上。
DRF的RetrieveAPIView默认会在查询不到对象时抛出NotFound异常,但前提是遵循视图的默认查询逻辑:get_queryset返回用于筛选的基础QuerySet,get_object负责从QuerySet中获取单个对象,当对象不存在时自动抛出异常。但你当前的get_queryset直接返回了针对单个ID的QuerySet(或缓存的QuerySet),并且schema生成工具不会执行视图的实际代码,只能通过视图的结构和默认行为推断可能的响应。
drf-standardized-errors的AutoSchema依赖drf-spectacular的逻辑识别错误响应,而drf-spectacular只有在明确识别到视图可能抛出对应异常时,才会生成响应文档。你的自定义get_queryset改变了默认逻辑,导致schema生成器无法识别潜在的404场景;而手动抛出NotFound时,工具能通过代码中的显式异常识别到这个响应,所以能生成对应的文档。
修复方案
方案一:修正视图逻辑(推荐)
将缓存逻辑移到get_object方法中,保留get_queryset的默认职责:
class MemberDetailsView(generics.RetrieveAPIView): serializer_class = MemberDetailsSerializer schema = AutoSchema() def get_queryset(self): return TE_MemberList.objects.all() def get_object(self): member_id = self.kwargs["pk"] cache_key = f"member_{member_id}" cached_member = cache.get(cache_key) if cached_member is None: print("NO CACHE") cached_member = super().get_object() cache.set(cache_key, cached_member, timeout=600) return cached_member
这样既保留了RetrieveAPIView自动抛出NotFound的逻辑,又实现了缓存,同时schema生成器能识别到默认的404场景。
方案二:显式声明响应
如果不想修改视图逻辑,可使用drf-spectacular的@extend_schema装饰器手动添加404响应:
from drf_spectacular.utils import extend_schema, OpenApiResponse from drf_standardized_errors.openapi_serializers import ClientErrorSerializer @extend_schema( responses={ 404: OpenApiResponse(response=ClientErrorSerializer), } ) class MemberDetailsView(generics.RetrieveAPIView): # 原有视图代码
内容的提问来源于stack exchange,提问作者Vladislav Dashtu

