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

Django REST中能否嵌套detail=True的Action?单用户衣物操作疑问

问题解答

一、可以实现,示例代码如下

你可以通过定义带额外URL参数的action来实现该需求,同时要注意校验资源归属避免越权。代码示例:

from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status
from rest_framework import viewsets
from .models import User, Clothes
from .serializers import UserOutfitSerializer, ClothesSerializer

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    # 原有的列出用户所有关联Clothes的action
    @action(detail=True, methods=['get'], serializer_class=UserOutfitSerializer)
    def outfit(self, request, pk=None):
        user = self.get_object()
        serializer = UserOutfitSerializer(user.clothes_set.all(), many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

    # 新增的单个Clothes操作action
    @action(detail=True, methods=['get', 'put', 'patch', 'delete'], 
            url_path='outfit/(?P<clothes_id>[^/.]+)', url_name='user-outfit-detail')
    def outfit_detail(self, request, pk=None, clothes_id=None):
        user = self.get_object()
        # 校验Clothes是否属于当前用户
        try:
            clothes = user.clothes_set.get(id=clothes_id)
        except Clothes.DoesNotExist:
            return Response({"detail": "该服饰不存在或不属于当前用户"}, 
                            status=status.HTTP_404_NOT_FOUND)
        
        if request.method == 'GET':
            serializer = ClothesSerializer(clothes)
            return Response(serializer.data)
        elif request.method in ['PUT', 'PATCH']:
            serializer = ClothesSerializer(clothes, data=request.data, partial=(request.method == 'PATCH'))
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        elif request.method == 'DELETE':
            clothes.delete()
            return Response({"detail": "服饰已删除"}, status=status.HTTP_204_NO_CONTENT)

关键细节:

  • 通过url_path='outfit/(?P<clothes_id>[^/.]+)'匹配路径中的clothes_id参数
  • 必须校验服饰与用户的归属关系,防止非法访问他人资源
  • 根据请求方法分别处理检索、更新、删除逻辑

二、关于是否属于不良开发实践

这种实现不算绝对的不良实践,但需要结合业务场景权衡:

适合的场景

  • API设计强调用户资源的聚合性,希望所有用户相关操作都挂载在/users路径下,保持语义连贯
  • 业务逻辑中,服饰操作必须强依赖所属用户,独立的/clothes接口没有实际意义

潜在问题

  1. 代码冗余:如果已有独立的ClothesViewSet,会重复实现CRUD逻辑,增加维护成本
  2. 路由复杂度:嵌套路由会让API结构更复杂,客户端需要记忆更长的路径
  3. 权限控制重复:需要在该action中额外校验资源归属,而独立ViewSet可以通过权限类统一处理

更常规的替代方案

如果业务允许,更符合RESTful规范的做法是:

  • 独立创建ClothesViewSet
  • 通过过滤条件(如URL参数user_id)返回特定用户的服饰
  • 用自定义权限类(如IsOwnerOrReadOnly)控制用户只能操作自己的服饰

这种方案更符合单一职责原则,代码复用性更高,也更贴近通用API设计习惯。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 05:55:26