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

时间:2015-08-07 14:30:04

标签: python django-rest-framework

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

class MyViewSet(viewsets.ViewSet):       

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

当我尝试PUT请求时,我得到一个错误,如方法PUT不允许。如果我使用def put(self, request):,一切正常。根据{{​​3}}我应该使用def update():而不是def put():,为什么会这样?

6 个答案:

答案 0 :(得分:29)

有时 POST和PUT不同,因为 PUT在网址中使用id 在这种情况下,yoy会收到此错误:“ PUT不允许”。

示例:

  • POST:/api/users/
  • PUT:/api/users/1/

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

答案 1 :(得分: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)

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

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

答案 2 :(得分:2)

这是因为FBSDKShareOpenGraphContent没有为Clipboard.SetText(Label1.Content+ Label2.Content+ Label3.Content+ Label4.Content+ Label5.Content+ Label6.Content+ Label7.Content) 方法定义处理程序,因此传入的请求无法映射到视图上的处理程序方法,从而引发异常。

(注意:APIView继承自.put()viewsets.ViewSet

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

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

dispatch()

由于您的视图中未定义APIView方法处理程序,因此DRF会调用回退处理程序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()例外。

.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 时,DRF可以将传入的请求方法映射到视图上的处理程序方法。这导致返回适当的响应而不会引发异常。

答案 3 :(得分:1)

这个答案是正确的,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

答案 4 :(得分:0)

def update(self, request, pk=None):
    data_in = request.data
    print(data_in)

    instance = self.get_object()
    serializer = self.get_serializer(instance, data=request.data, partial=False)
    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()
    data_out = serializer.data
    return Response(serializer.data)

答案 5 :(得分:0)

使用 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")