You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 09:59:53