Django REST框架:使用def update()在ViewSet中不允许使用方法PUT

flo*_*urr 13 python django-rest-framework

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

class MyViewSet(viewsets.ViewSet):       

    def update(self, request):
        # do things...
        return Response(status=status.HTTP_200_OK)
Run Code Online (Sandbox Code Playgroud)

当我尝试PUT请求时,我得到一个错误,如方法PUT不允许.如果我使用def put(self, request):所有的东西工作正常.因此到的文档,我应该使用def update():没有def put():,为什么会发生?

yes*_*ema 38

有时它对于POST和PUT是不同的,因为PUT在URL中使用id 在这种情况下,yoy会得到这个错误:" PUT不允许 ".

例:

  • POST: /api/users/
  • 放: /api/users/1/

希望它能为某人节省大量时间

  • 请注意,如果您只定义方法 `put`(而不是通过 DRF mixin 定义 `update`),那么它将在没有 ID 的情况下工作。 (2认同)
  • 就我而言,这就像一个魅力,因为我使用了与之相关的“viewsets.ModelViewSet”和“serializers.ModelSerializer”。 (2认同)

Kos*_*tyn 7

此代码有类似的"方法PUT不允许"问题,因为请求中缺少"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)
Run Code Online (Sandbox Code Playgroud)

原来我在序列化器字段中错过了'id',因此PUT请求无法为记录提供id.序列化器的固定版本如下:

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


fel*_*lix 6

这个答案是正确的,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
Run Code Online (Sandbox Code Playgroud)


Rah*_*pta 5

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

(注:viewsets.ViewSet继承自ViewSetMixinAPIView

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

根据类中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
Run Code Online (Sandbox Code Playgroud)

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

的源代码.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 
Run Code Online (Sandbox Code Playgroud)

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

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

  • 您正在调用的 URL 是否包含详细信息“pk”路径参数?原因调用 PUT 到根资源,例如 /users/ 没有 /users/<pk> 将导致方法不允许。 (3认同)
  • 这种方法应该始终有效。请检查您是否按照文档中的说明正确定义了 `viewsets` 和 `routers`,因为 `router` 本身会将 `put` 函数设置为指向 `update` 函数。因此,每当 PUT 请求到来时,处理程序将执行 .update() 操作的代码。 (2认同)