Python Django REST framework:在具有def update()的ViewSet中不允许使用方法PUT

Python Django REST framework:在具有def update()的ViewSet中不允许使用方法PUT,python,django-rest-framework,Python,Django Rest Framework,在DRF中,我有一个简单的视图集,如下所示: class MyViewSet(viewsets.ViewSet): def update(self, request): # do things... return Response(status=status.HTTP_200_OK) 当我尝试一个PUT请求时,我得到了一个错误,比如方法PUT not allowed。如果我使用def put(self,request):一切正常。因此,我

在DRF中,我有一个简单的视图集,如下所示:

class MyViewSet(viewsets.ViewSet):       

    def update(self, request):
        # do things...
        return Response(status=status.HTTP_200_OK)

当我尝试一个PUT请求时,我得到了一个错误,比如方法PUT not allowed。如果我使用
def put(self,request):
一切正常。因此,我应该使用
def update():
而不是
def put():
,为什么会发生这种情况?

这是因为
APIView
没有为
.put()
方法定义处理程序,因此传入的请求无法映射到视图上的处理程序方法,从而引发异常。

(注意:
viewsets.ViewSet
继承自
ViewSetMixin
apieview

APIView
中的
dispatch()
方法检查是否为请求
方法定义了方法处理程序。如果
dispatch()
方法找到请求方法的处理程序,它将返回相应的响应。否则,它将引发一个异常

根据
APIView
类中
dispatch()
方法的源代码:

def dispatch(self, request, *args, **kwargs):       
        ...
        ...    
        try:
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                 # here handler is fetched for the request method
                 # `http_method_not_allowed` handler is assigned if no handler was found
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed 

            response = handler(request, *args, **kwargs) # handler is called here

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
由于视图中未定义
.put()
方法处理程序,因此DRF调用回退处理程序
.http\u method\u not\u allowed
。这会引发
方法不允许的
异常

.http\u method\u not\u allowed()
的源代码是:

def http_method_not_allowed(self, request, *args, **kwargs):
    """
    If `request.method` does not correspond to a handler method,
    determine what kind of exception to raise.
    """
    raise exceptions.MethodNotAllowed(request.method) # raise an exception 
当您在视图中定义
.put()
时,它为什么起作用?

在视图中定义
defput(self,request):
时,DRF可以将传入的请求方法映射到视图上的处理程序方法。这导致在没有引发异常的情况下返回相应的响应。

此代码存在类似的“不允许方法放置”问题,因为请求中缺少“id”:

class ProfileStep2Serializer(serializers.ModelSerializer):
    class Meta:
        model = Profile
        fields = ('middle_initial', 'mobile_phone', 'address', 'apt_unit_num', 'city', 'state', 'zip')

class Step2ViewSet(viewsets.ModelViewSet):
    serializer_class = ProfileStep2Serializer

    def get_queryset(self):
        return Profile.objects.filter(pk=self.request.user.profile.id)
事实证明,我在序列化程序字段中遗漏了“id”,因此PUT请求无法为记录提供id。序列化程序的固定版本如下所示:

class ProfileStep2Serializer(serializers.ModelSerializer):
    class Meta:
        model = Profile
        fields = ('id', 'middle_initial', 'mobile_phone', 'address', 'apt_unit_num', 'city', 'state', 'zip')
将需求
id
放入URL 有时POST和PUT之间存在差异,因为PUT需要
id
在URL中 这就是为什么会出现错误:“不允许放置”

例如:

  • POST
    /api/users/
  • PUT
    /api/users/1/
希望它能为某人节省大量时间

这个答案是正确的,不允许使用PUT,因为DRF希望实例id位于URL中。也就是说,在视图集中使用此mixin可能是修复它的最佳方法(从下面粘贴的副本)


使用Django视图集,您可以轻松地将方法映射到url中,例如

path('createtoken/', CreateTokenView.as_view({'post': 'create', 'put':'update'}))
然后在您的类中,根据您的意愿重写这些方法:

class CreateTokenView(viewsets.ModelViewSet):
    queryset = yourSet.objects.all()
    serializer_class = yourSerializer

    def create(self, request, *args, **kwargs):
        #any method you want here
        return Response("response")

    def update(self, request, *args, **kwargs):
        # any method you want here
        return Response("response")

因此,如果我想使用create,我只需要映射它?在您的视图中定义
update()
时,您是否在URL中定义了
router
。请检查您是否按照文档中的说明正确定义了
视图集
路由器
,因为
路由器
本身将
放置
功能设置为指向
更新
功能。因此,每当
PUT
请求到来时,处理程序将执行
.update()
操作的代码。您调用的URL是否包含详细的“pk”路径参数?原因调用根资源的PUT,例如/users/without/users/将导致不允许使用方法。请注意,如果您只定义方法
PUT
(而不是通过DRF mixin
update
),那么它将在没有ID的情况下工作。当然,但不是一直都可以;设想一个“单例”资源,没有ID。如果您有一个代表当前登录用户的
/user/me
端点,这可能是一个常见的用例。确实节省了很多时间。谢谢你在我的例子中,这很有魅力,因为我使用了一个
viewset.ModelViewSet
和一个与之相关的
serializer.ModelSerializer
。你也可以使用
PATCH
方法来执行Django模型的部分更新。这个代码片段可以解决这个问题,确实有助于提高你文章的质量。记住,你是在为将来的读者回答这个问题,那些人可能不知道你的代码建议的原因。你真是个好人,你为我节省了很多时间!!
path('createtoken/', CreateTokenView.as_view({'post': 'create', 'put':'update'}))
class CreateTokenView(viewsets.ModelViewSet):
    queryset = yourSet.objects.all()
    serializer_class = yourSerializer

    def create(self, request, *args, **kwargs):
        #any method you want here
        return Response("response")

    def update(self, request, *args, **kwargs):
        # any method you want here
        return Response("response")