django rest框架PUT返回404而不是创建一个对象

时间:2015-02-09 15:35:54

标签: django django-rest-framework

我希望能够使用相同的请求创建或更新对象。操作应该是幂等的。

如果对象存在,则向DRF发送PUT请求按预期工作但如果对象不存在,我会得到404而不是创建它。

models.py:

class Btilog(models.Model):
    md5hash = models.CharField(primary_key=True, max_length=32)
    vteip = models.ForeignKey('vte.VTE')
    timestamp = models.DateTimeField(blank=False)
    source = models.TextField()
    code = models.CharField(max_length=10, blank=False)
    msg = models.TextField(blank=False)

api.py:

class BtilogSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Btilog

class BtilogVSet(viewsets.ModelViewSet):
    queryset = models.Btilog.objects.all()
    serializer_class = BtilogSerializer
    permission_classes = (permissions.AllowAny,)

urls.py:

...
router = routers.DefaultRouter()
router.register(r'btilog', api.BtilogVSet)

urlpatterns = patterns('',
    url(r'^api/', include(router.urls)),
    ...
)

请求失败

http --form PUT http://192.168.10.121:8888/logger/api/btilog/60c6b9e99c43c0bf4d8bc22d671169b1/ vteip='172.25.128.85' 'code'='Test' 'md5hash'='60c6b9e99c43c0bf4d8bc22d671169b1' 'timestamp'='2015-05-31T13:34:01' msg='Test' source='Test'
HTTP/1.0 404 NOT FOUND
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Type: application/json
Date: Mon, 09 Feb 2015 15:16:47 GMT
Server: WSGIServer/0.1 Python/2.7.6
Vary: Accept, Cookie

{
    "detail": "Not found"
    }

如下所述:http://restcookbook.com/HTTP%20Methods/put-vs-post/ put的正确行为应该是创建对象(如果它不存在)。

使用DRF的Browsable API工具发出同样的错误来发出请求。 DRF的行为也一样吗?我做错了什么?

2 个答案:

答案 0 :(得分:3)

好吧,也许你应该尝试覆盖你的modelviewset中的update方法,它处理PUT http方法:

class BtilogVSet(viewsets.ModelViewSet):
    queryset = models.Btilog.objects.all()
    serializer_class = BtilogSerializer
    permission_classes = (permissions.AllowAny,)

    def update(self, request, *args, **kwargs):
        try:
            instance = Btilog.objects.get(pk=kwargs['pk'])
            serializer = serializers.BtilogSerializer(instance=instance,data=request.data)
            if serializer.is_valid():
                btilog=serializer.save()
                return Response(serializer.data,status=status.HTTP_200_OK)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        except Btilog.DoesNotExist:
            serializer = serializers.BtilogSerializer(data=request.data)
        if serializer.is_valid():
            btilog=serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

答案 1 :(得分:0)

是的,当我了解到我对Django Rest Framework的PUT返回的是404而不是创建对象时,我对OP感到惊讶。 问题是PUT仅映射到“更新”操作(在urls.py中使用DefaultRouter时),并且更新操作确实希望该对象存在。因此,您必须稍微修改更新功能(来自rest_framework.mixins.UpdateModelMixin)以处理创建当前不存在的对象。

对于这个问题,我来得有点晚,所以也许这可以帮助使用Django Rest Framework更高版本的人,我的版本是v3.9.4。

如果您使用的是ModelViewSet,那么我建议在您的class viewset中的views.py文件中插入以下更新函数: 它只是DRF现有更新和创建mixins的混合,并且您还可以使用这些mixins进行一些额外的检查(权限检查,get_serializer_class等)。此外,由于它不包含对模型的引用,因此它更具可移植性,-对DRF开发人员做得很好(再次)。您将需要导入Http404和ValidationError,如下所示。

from django.http import Http404
from rest_framework import status
from rest_framework.exceptions import ValidationError


class BtilogVSet(viewsets.ModelViewSet):
    queryset = models.Btilog.objects.all()
    serializer_class = BtilogSerializer
    permission_classes = (permissions.AllowAny,)        

    def update(self, request, *args, **kwargs):  
        partial = kwargs.pop('partial', False)
        try:
            instance = self.get_object()  #throws a Http404 if instance not found
            serializer = self.get_serializer(instance, data=request.data, partial=partial)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)
            if getattr(instance, '_prefetched_objects_cache', None):
                # If 'prefetch_related' has been applied to a queryset, we need to
                # forcibly invalidate the prefetch cache on the instance.
                instance._prefetched_objects_cache = {}
            return Response(serializer.data)
        except Http404:
            #create the object if it has not been found
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True) # will throw ValidationError
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
        except ValidationError:  # typically serializer is not valid
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        except:
            raise

注意,PATCH也通过函数间接映射到update()函数 partial_update()。您无需在下面包括partial_update代码,它由文件rest_framework.mixins.UpdateModelMixin提供,它是ModelViewSet的混合。

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