Adding PATCH support to Django REST Framework

This commit is contained in:
Andrew Hankinson 2012-12-16 14:49:18 -05:00
parent 4f96951be9
commit 18338a37d3
5 changed files with 68 additions and 7 deletions

View File

@ -47,14 +47,14 @@ class GenericAPIView(views.APIView):
return serializer_class return serializer_class
def get_serializer(self, instance=None, data=None, files=None): def get_serializer(self, instance=None, data=None, files=None, partial=False):
""" """
Return the serializer instance that should be used for validating and Return the serializer instance that should be used for validating and
deserializing input, and for serializing output. deserializing input, and for serializing output.
""" """
serializer_class = self.get_serializer_class() serializer_class = self.get_serializer_class()
context = self.get_serializer_context() context = self.get_serializer_context()
return serializer_class(instance, data=data, files=files, context=context) return serializer_class(instance, data=data, files=files, partial=partial, context=context)
class MultipleObjectAPIView(MultipleObjectMixin, GenericAPIView): class MultipleObjectAPIView(MultipleObjectMixin, GenericAPIView):
@ -169,7 +169,17 @@ class UpdateAPIView(mixins.UpdateModelMixin,
Concrete view for updating a model instance. Concrete view for updating a model instance.
""" """
def put(self, request, *args, **kwargs): def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs) return self.update(request, partial=False, *args, **kwargs)
class ParitalUpdateAPIView(mixins.UpdateModelMixin,
SingleObjectAPIView):
"""
Concrete view for paritally updating a model instance.
"""
def patch(self, request, *args, **kwargs):
return self.update(request, partial=True, *args, **kwargs)
class ListCreateAPIView(mixins.ListModelMixin, class ListCreateAPIView(mixins.ListModelMixin,
@ -209,7 +219,29 @@ class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
return self.retrieve(request, *args, **kwargs) return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs): def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs) return self.update(request, partial=False, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
class RetrievePartialUpdateDestroyAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
SingleObjectAPIView):
"""
Concrete view for retrieving, updating via PATCH (partial) or PUT (full),
or deleting a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, partial=False, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.update(request, partial=True, *args, **kwargs)
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs) return self.destroy(request, *args, **kwargs)

View File

@ -81,7 +81,7 @@ class UpdateModelMixin(object):
Update a model instance. Update a model instance.
Should be mixed in with `SingleObjectBaseView`. Should be mixed in with `SingleObjectBaseView`.
""" """
def update(self, request, *args, **kwargs): def update(self, request, partial=False, *args, **kwargs):
try: try:
self.object = self.get_object() self.object = self.get_object()
created = False created = False
@ -89,7 +89,7 @@ class UpdateModelMixin(object):
self.object = None self.object = None
created = True created = True
serializer = self.get_serializer(self.object, data=request.DATA, files=request.FILES) serializer = self.get_serializer(self.object, data=request.DATA, files=request.FILES, partial=partial)
if serializer.is_valid(): if serializer.is_valid():
self.pre_save(serializer.object) self.pre_save(serializer.object)

View File

@ -63,6 +63,20 @@ class DecoratorTestCase(TestCase):
response = view(request) response = view(request)
self.assertEqual(response.status_code, 405) self.assertEqual(response.status_code, 405)
# def test_calling_patch_method(self):
# @api_view(['GET', 'PATCH'])
# def view(request):
# return Response({})
# request = self.factory.patch('/')
# response = view(request)
# self.assertEqual(response.status_code, 200)
# request = self.factory.post('/')
# response = view(request)
# self.assertEqual(response.status_code, 405)
def test_renderer_classes(self): def test_renderer_classes(self):
@api_view(['GET']) @api_view(['GET'])

View File

@ -180,6 +180,19 @@ class TestInstanceView(TestCase):
updated = self.objects.get(id=1) updated = self.objects.get(id=1)
self.assertEquals(updated.text, 'foobar') self.assertEquals(updated.text, 'foobar')
# def test_patch_instance_view(self):
# """
# PATCH requests to RetrieveUpdateDestroyAPIView should update an object.
# """
# content = {'text': 'foobar'}
# request = factory.patch('/1', json.dumps(content),
# content_type='application/json')
# response = self.view(request, pk=1).render()
# self.assertEquals(response.status_code, status.HTTP_200_OK)
# self.assertEquals(response.data, {'id': 1, 'text': 'foobar'})
# updated = self.objects.get(id=1)
# self.assertEquals(updated.text, 'foobar')
def test_delete_instance_view(self): def test_delete_instance_view(self):
""" """
DELETE requests to RetrieveUpdateDestroyAPIView should delete an object. DELETE requests to RetrieveUpdateDestroyAPIView should delete an object.

View File

@ -18,7 +18,7 @@ class BasicView(APIView):
return Response({'method': 'POST', 'data': request.DATA}) return Response({'method': 'POST', 'data': request.DATA})
@api_view(['GET', 'POST', 'PUT']) @api_view(['GET', 'POST', 'PUT', 'PATCH'])
def basic_view(request): def basic_view(request):
if request.method == 'GET': if request.method == 'GET':
return {'method': 'GET'} return {'method': 'GET'}
@ -26,6 +26,8 @@ def basic_view(request):
return {'method': 'POST', 'data': request.DATA} return {'method': 'POST', 'data': request.DATA}
elif request.method == 'PUT': elif request.method == 'PUT':
return {'method': 'PUT', 'data': request.DATA} return {'method': 'PUT', 'data': request.DATA}
elif request.method == 'PATCH':
return {'method': 'PATCH', 'data': request.DATA}
def sanitise_json_error(error_dict): def sanitise_json_error(error_dict):