DRF ViewSet的create方法被DefaultRouter更名或忽略相关问题咨询
问题1:上述代码是否在旧版本DRF中可以正常运行?
- 不能,DRF 3.x所有版本的路由生成逻辑都遵循相同的标准动作映射规则,这段代码的核心问题是继承类错误,和DRF版本无关。
你当前的CurrentUserViewSet继承的是ViewSetMixin+ Django原生View,而DRF的ViewSet要求必须继承APIView子类(通常是GenericAPIView)才能让标准动作映射、请求解析、权限校验等逻辑正常生效,原生Django View不支持DRF的动作分发机制。
问题2:我定义的create路由为什么会被更改为list?
- 本质是两个原因共同导致的:
- 路由命名规则问题:DRF的
DefaultRouter对于列表级根路径(前缀/me/),默认会为该路径生成多HTTP方法映射:GET请求绑定list方法,POST请求绑定create方法,该路由的name统一用{basename}-list,这是DRF的固定命名规则,不是你的create方法被重命名成list,你看到的是路由的name属性,不是绑定的处理方法名。 - 继承类错误导致方法不生效:你的视图继承的是Django原生
View,原生View的请求分发逻辑是直接找和HTTP方法同名的方法(比如POST请求就找post方法),根本不认create/list这类DRF标准动作方法,所以即使路由生成了映射,请求进来也会返回405错误,永远触发不到create方法。
- 路由命名规则问题:DRF的
问题3:若额外定义名为list的路由,为什么create路由会被DefaultRouter忽略?
- 不会被忽略,只是没有单独显示而已。
DefaultRouter不会为同一个路径下的不同HTTP方法生成单独的URLPattern条目,GET和POST请求的/me/路径是同一个URLPattern条目,只是内部绑定了不同的处理方法,你打印router.urls只会看到一个条目,不会分开显示。你可以打印路由条目的callback属性查看绑定的方法映射,就能看到POST对应的create方法。 - 如果你定义了list方法之后POST还是到不了create,还是继承类错误的问题,和路由生成无关。
问题4:无需传入pk参数、直接使用request.user更新当前已认证用户的更优实现方式是什么?
推荐两种实现方式,按需选择:
- 自定义action实现:逻辑最清晰,不需要依赖标准动作映射规则:
from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response # 替换为你自己的用户序列化器 from .serializers import UserSerializer class CurrentUserViewSet(viewsets.GenericViewSet): permission_classes = [IsAuthenticated] serializer_class = UserSerializer def get_object(self): return self.request.user @action(detail=False, methods=["put", "patch"]) def update_profile(self, request): instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=request.method == "PATCH") serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data)这种写法会生成
/me/update_profile/的路由,用PUT/PATCH请求提交修改,不需要传pk,逻辑完全自解释。- 复用标准update方法:如果想复用DRF标准的更新逻辑,可以重写
get_object方法:
from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated from .serializers import UserSerializer from django.contrib.auth import get_user_model User = get_user_model() class CurrentUserViewSet(viewsets.ModelViewSet): permission_classes = [IsAuthenticated] serializer_class = UserSerializer # 禁用不需要的HTTP方法,避免暴露不必要的接口 http_method_names = ["get", "put", "patch"] def get_queryset(self): # 强制只返回当前用户的查询集,避免越权风险 return User.objects.filter(id=self.request.user.id) def get_object(self): return self.request.user这种写法用PUT/PATCH请求
/me/即可更新当前用户,不需要额外定义路由。
内容的提问来源于stack exchange,提问作者nttaylor
相关产品推荐
相关产品推荐

