我希望能够使用相同的请求创建或更新对象。操作应该是幂等的。
如果对象存在,则向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的行为也一样吗?我做错了什么?
答案 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)