Python Django REST 框架:在带有 def update() 的 ViewSet 中不允许方法 PUT

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31880227/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 10:41:54  来源:igfitidea点击:

Django REST framework: method PUT not allowed in ViewSet with def update()

pythondjango-rest-framework

提问by floatingpurr

In DRF, I have a simple ViewSet like this one:

在 DRF 中,我有一个像这样的简单 ViewSet:

class MyViewSet(viewsets.ViewSet):       

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

When I try a PUT request, I get an error like method PUT not allowed. If I use def put(self, request):all things work fine. Accordingly to the docsI should use def update():not def put():, why does it happen?

当我尝试 PUT 请求时,我收到类似方法 PUT not allowed 的错误。如果我使用def put(self, request):一切正常。因此到的文档,我应该使用def update():没有def put():,为什么会发生?

采纳答案by Rahul Gupta

This is because the APIViewhas no handler defined for .put()method so the incoming request could not be mapped to a handler method on the view, thereby raising an exception.

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

(Note: viewsets.ViewSetinherit from ViewSetMixinand APIView)

(注:viewsets.ViewSet继承自ViewSetMixinAPIView

The dispatch()method in the APIViewchecks if a method handler is defined for the request method.If the dispatch()method finds a handler for the request method, it returns the appropriate response. Otherwise, it raises an exception MethodNotAllowed.

中的dispatch()方法APIView检查是否为请求定义了方法处理程序method。如果该dispatch()方法找到请求方法的处理程序,则返回适当的响应。否则,它会引发异常MethodNotAllowed

As per the source code of dispatch()method in the APIViewclass:

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

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

Since .put()method handler is not defined in your view, DRF calls the fallback handler .http_method_not_allowed. This raises an MethodNotAllowedexception.

由于.put()方法处理程序未在您的视图中定义,DRF 调用回退处理程序.http_method_not_allowed。这引发了一个MethodNotAllowed例外。

The source code for .http_method_not_allowed()is:

的源代码.http_method_not_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 

Why it worked when you defined .put()in your view?

当您.put()在视图中定义时,为什么它有效?

When you defined def put(self, request):in your view, DRF could map the incoming request method to a handler method on the view. This led to appropriate response being returned without an exception being raised.

当您def put(self, request):在视图中定义时,DRF 可以将传入的请求方法映射到视图上的处理程序方法。这导致返回适当的响应,而不会引发异常。

回答by Kostyantyn

Had a similar "Method PUT not allowed" issue with this code, because 'id' was missing in the request:

这段代码有一个类似的“Method PUT not allowed”问题,因为请求中缺少“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)

Turned out that i have missed 'id' in the serializer fields, so PUT request was NOT able to provide an id for the record. The fixed version of the serializer is below:

原来我在序列化器字段中遗漏了“id”,因此 PUT 请求无法为记录提供 id。序列化程序的固定版本如下:

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

回答by yestema

Sometimesit is different for POST and PUT, because PUT uses id in URLIn this case yoy'll get this error: "PUT is not Allowed".

有时POST 和 PUT 是不同的,因为PUT 在 URL 中使用 id在这种情况下,你会得到这个错误:“ PUT is not Allowed”。

Example:

例子:

  • POST: /api/users/
  • PUT: /api/users/1/
  • 邮政: /api/users/
  • 放: /api/users/1/

Hope it'll save a lot of time for somebody

希望它会为某人节省很多时间

回答by felix

This answer is right, Django REST framework: method PUT not allowed in ViewSet with def update(), PUT is not allowed, because DRF expects the instance id to be in the URL. That being said, using this mixin in your ViewSet is probably the best way to fix it (from https://gist.github.com/tomchristie/a2ace4577eff2c603b1bcopy pasted below)

这个答案是对的,Django REST framework: method PUT not allowed in ViewSet with def update(), PUT 是不允许的,因为 DRF 期望实例 ID 在 URL 中。话虽如此,在您的 ViewSet 中使用这个 mixin 可能是修复它的最佳方法(来自https://gist.github.com/tomchristie/a2ace4577eff2c603b1b复制粘贴在下面)

class AllowPUTAsCreateMixin(object):
    """
    The following mixin class may be used in order to support PUT-as-create
    behavior for incoming requests.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object_or_none()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)

        if instance is None:
            lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
            lookup_value = self.kwargs[lookup_url_kwarg]
            extra_kwargs = {self.lookup_field: lookup_value}
            serializer.save(**extra_kwargs)
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        serializer.save()
        return Response(serializer.data)

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

    def get_object_or_none(self):
        try:
            return self.get_object()
        except Http404:
            if self.request.method == 'PUT':
                # For PUT-as-create operation, we need to ensure that we have
                # relevant permissions, as if this was a POST request.  This
                # will either raise a PermissionDenied exception, or simply
                # return None.
                self.check_permissions(clone_request(self.request, 'POST'))
            else:
                # PATCH requests where the object does not exist should still
                # return a 404 response.
                raise