You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Django REST Framework Generic ViewSet中如何让不同detail值的GET动作共用相同的url_path与url_name

解决DRF ViewSet中两个不同detail值的Action共用相同url_path的问题

我明白你想要实现的效果:一个列表级的/users接口(detail=False)和一个详情级的/<user-id>/users接口(detail=True),两者使用相同的url路径后缀,但现在后者访问不了。问题的根源在于DRF自动生成路由时,相同的url_name会导致路由覆盖——当你给两个Action设置相同的url_name时,DRF只会保留最后注册的那个路由(通常detail=False的Action会先被处理,所以detail=True的会被覆盖),导致你访问详情级路径时找不到对应的视图。

下面给你两种解决方案,根据你的需求选择:

方案一:保持url_path相同,设置不同的url_name(推荐)

这是最简洁的方案,因为url_name主要用于反向解析路由,只要路径符合你的要求,不同的url_name完全不影响接口访问。修改代码如下:

from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

class CustomViewSet(viewsets.GenericViewSet):
    # 因为没有使用模型,无需指定queryset和serializer_class

    @action(methods=["get"], detail=False, url_path="users", url_name="users-list")
    def get_users(self, request):
        # 业务代码:返回用户列表
        return Response({"message": "用户列表接口"})

    @action(methods=["get"], detail=True, url_path="users", url_name="users-detail")
    def get_user(self, request, pk):  # DRF默认用pk作为详情路由参数名,可通过lookup_field自定义
        # 业务代码:返回指定用户的相关数据
        return Response({"message": f"单个用户接口,用户ID: {pk}"})

这样设置后,两个接口的路径完全符合你的需求:

  • 列表级:{{base_url}}/{{prefix}}/users
  • 详情级:{{base_url}}/{{prefix}}/<pk>/users

由于url_name不同,DRF会正确注册两个路由,不会出现覆盖问题,同时也不影响后续的反向解析操作。

方案二:手动注册路由,实现相同的url_name(不推荐,除非必要)

如果你坚持要让两个Action使用相同的url_name,可以绕过DRF的自动路由生成,手动在urls.py中注册路由。这种方式虽然能实现,但不够灵活,后续修改Action时需要同步修改路由配置。

首先修改ViewSet代码,可去掉@action装饰器中的url_pathurl_name

class CustomViewSet(viewsets.GenericViewSet):
    def get_users(self, request):
        return Response({"message": "用户列表接口"})

    def get_user(self, request, pk):
        return Response({"message": f"单个用户接口,用户ID: {pk}"})

然后在urls.py中手动添加路由:

from django.urls import path
from .views import CustomViewSet

# 注册列表级视图
list_view = CustomViewSet.as_view({'get': 'get_users'})
# 注册详情级视图
detail_view = CustomViewSet.as_view({'get': 'get_user'})

urlpatterns = [
    # 列表级路由,url_name设为"users"
    path('<prefix>/users', list_view, name='users'),
    # 详情级路由,同样url_name设为"users"
    path('<prefix>/<int:pk>/users', detail_view, name='users'),
]

需要注意的是,这种方式下使用reverse反向解析路由时,需要通过传递是否带pk参数来区分,否则可能出现解析错误,这也是不推荐该方案的原因。

最后补充:你提到视图集没有使用任何模型,所以无需继承ModelViewSet,使用GenericViewSet或者直接ViewSet即可,不用额外指定querysetserializer_class,不影响功能使用。

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

火山引擎 最新活动