Reputation: 497
I am trying to add update options to the list items. So that if anyone perform 'PATCH' request to it I will get the details and update them. This is my code for the implementation
class SwitchListView(UpdateModelMixin, ListAPIView):
serializer_class = serializers.SwitchSerializer
lookup_field = 'home_id'
def get_queryset(self):
home_id = self.kwargs.get('home_id', None)
if home_id is None or int(home_id) < 0 or \
self.request.user.pk != models.Home.objects.filter(pk=home_id)[0].user.pk:
return models.Switch.objects.none()
query = models.Switch.objects.filter(home=models.Home.objects.filter(pk=home_id))
return query
def get(self, request, *args, **kwargs):
return super(SwitchListView, self).get(request, *args, **kwargs)
def partial_update(self, request, *args, **kwargs):
print("Came here")
data = request.data['data']
for i in data:
query = self.get_queryset().filter(i['pk'])
if query.exists():
query.switch_status = i['switch_status']
query.save()
return Response({'message': 'successfully updated switch!'})
But here the request to the api is only accepting GET, HEAD and OPTIONS. I even tried adding http_method_names = ('get', 'patch')
but even this is not working!!
Is there any way to put the patch request to the view ?
Thanks
Upvotes: 4
Views: 7073
Reputation: 12903
Here is my implementation, FWIW:
from rest_framework import viewsets
from rest_framework.response import Response
class MyListView(viewsets.mixins.ListModelMixin, viewsets.GenericViewSet):
serializer_class = ...
queryset = MyModel.objects.all()
def list_update(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
instances = list(queryset)
count = len(instances)
# TODO perhaps validate for max allowed count, before doing list(queryset) !
data = [request.data] * count
serializer = self.get_serializer(
instances, data, many=True, partial=True)
serializer.is_valid(raise_exception=True)
self.perform_list_update(serializer)
return Response(status=204)
def perform_list_update(self, serializer):
for instance, data in zip(
serializer.instance, serializer.validated_data):
for attr, value in data.items():
setattr(instance, attr, value)
instance.save()
# alternatively use a queryset.update, but be aware that it will not
# fire pre_save and post_save signals
If you're not using DRF routers (because when doing anything "custom" like this, using only naked views is usually much more pain-free), edit urls.py like so:
urlpatterns = [
...
re_path(
r'^path/to/mymodels',
MyListView.as_view({
'get': 'list',
'patch': 'list_update', # <--
}),
),
]
If using routers, this little hack works and is simple, but not terribly nice:
router = routers.DefaultRouter(trailing_slash=False)
router.routes[0].mapping['patch'] = 'list_update' # <--
...
It might also makes sense to override get_serializer_class
on the viewset to have a different serializer for list_update action.
Upvotes: 4
Reputation: 6529
I based my version on frnhr's but didn't want to use GenericViewSet
. I also tried to make it conform a bit more to the existing generic views, using put
and patch
so I wouldn't need to mess with routers.
class FooBulkUpdateAPIView(ListAPIView):
queryset = Foo.objects.all()
serializer_class = FooBulkUpdateSerializer
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instances = self.filter_queryset(self.get_queryset())
data = [request.data] * instances.count()
serializer = self.get_serializer(instances, data, many=True, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(status=204)
def perform_update(self, serializer):
for instance, data in zip(serializer.instance, serializer.validated_data):
for attr, value in data.items():
setattr(instance, attr, value)
instance.save()
Upvotes: 0
Reputation: 51
from rest_framework.decorators import detail_route
...
@detail_route(methods=['put', 'patch'])
def partial_update(self, request, *args, **kwargs):
...
try it?
Upvotes: -2