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

DRF中url_path相同的action视图未在drf-yasg Swagger中全部展示

问题根因

  1. 代码逻辑错误:retrieve_favorite 对应的@action设置了detail=True,但实际业务逻辑是获取当前登录用户的收藏房源,不需要传入房源ID,参数配置和实际逻辑不匹配,且函数内部返回Response时漏写了return关键字,运行时会直接报错。
  2. drf-yasg的schema生成逻辑限制:对于url_path相同但拆分定义的多个@action,默认会出现识别覆盖,只会展示后注册的那一个接口。
  3. 两个@action的detail属性不一致,生成的实际URL路径存在差异(一个带<pk>参数,一个不带),进一步加剧了drf-yasg的识别冲突。

最优解决方案

将相同URL路径下不同请求方法的逻辑合并到同一个@action中,符合DRF的设计规范,也能完全规避接口文档展示冲突问题,修改后的代码如下:

from django.utils.decorators import method_decorator
from drf_yasg.utils import no_body, swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.mixins import ListModelMixin
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.status import (HTTP_201_CREATED,
                                HTTP_204_NO_CONTENT,
                                HTTP_404_NOT_FOUND,
                                HTTP_200_OK
                                )
from rest_framework.viewsets import GenericViewSet

from housery.house.models import House
from utils.drf_params import app_id
from utils.mixins import FilterQueryByHouse

from .serializers import HouseSerializer


@method_decorator(name="list", decorator=swagger_auto_schema(
    manual_parameters=[app_id]
    ))
class HouseViewSet(FilterQueryByHouse, ListModelMixin, GenericViewSet):
    queryset = House.objects.filter().prefetch_related(
        "opening_hours", "special_opening_hours__opening_hours",
    )
    serializer_class = HouseSerializer

    def get_permissions(self):
        if self.action in ["list"]:
            return [AllowAny()]
        return [IsAuthenticated()]

    # 合并原GET/DELETE两个action的逻辑
    @action(methods=["get", "delete"], detail=False, url_path="favorite", url_name="favorite")
    def handle_favorite(self, request):
        """
        get: Retrieve favorite house
        delete: Remove favorite house
        """
        if request.method == "GET":
            if request.user.favorite_house_id:
                instance = request.user.favorite_house
                # 补上漏写的return关键字
                return Response(self.get_serializer(instance=instance).data, status=HTTP_200_OK)
            return Response(status=HTTP_404_NOT_FOUND)
        elif request.method == "DELETE":
            request.user.favorite_house = None
            request.user.save()
            return Response(status=HTTP_204_NO_CONTENT)

    @swagger_auto_schema(request_body=no_body)
    @action(methods=["post"], detail=True)
    def favorite(self, request, pk):
        """set given house id as favorite house"""
        instance = self.get_object()
        request.user.favorite_house = instance
        request.user.save()
        return Response(self.get_serializer(instance=instance).data, status=HTTP_201_CREATED)

修改后GET和DELETE方法会自动合并到同一个/houses/favorite/路径的接口条目下,在swagger文档中可以正常切换请求方法查看,不会再出现覆盖展示的问题。

可选解决方案(不合并代码)

如果坚持要拆分成两个独立的函数实现,可以给每个@action单独加上swagger_auto_schema装饰器,指定不同的operation_id强制drf-yasg识别为不同接口:

@swagger_auto_schema(operation_id="retrieve_favorite")
@action(methods=["get"], detail=False, url_path="favorite", url_name="favorite-read")
def retrieve_favorite(self, request):
    # 原有逻辑,补全return
    pass

@swagger_auto_schema(operation_id="unset_favorite")
@action(methods=["delete"], detail=False, url_path="favorite", url_name="favorite-remove")
def unset_favorite(self, request):
    # 原有逻辑
    pass

注意要同时把两个@action的detail属性统一设置为False,避免路径不匹配的问题。

内容的提问来源于stack exchange,提问作者coredumped0x

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 08:06:03