Reputation: 5597
I'm trying to write a simple update view for my Django REST application, but I cannot figure out how to do it. I've gotten a range of errors from 403 to 500.
Basically, I have a table 'Foo' and I want to set the 'active' field from 'True' to 'False'. Here's the view I have currently:
class DisableFoo(generics.UpdateAPIView):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = FooSerializer
queryset = Foo.objects.all()
def perform_update(self, serializer):
queryset = Foo.objects.filter(pk = self.request.data['id'])
queryset.update(active = False)
However, this results in AssertionError:
Expected view DisableFoo to be called with a URL keyword argument named "pk".
Fix your URL conf, or set the '.lookup_field' attribute on the view correctly.
Upvotes: 3
Views: 1266
Reputation: 16010
You should not send the id
of the row you want to update with request.data but within the url
.
So if you are hitting right now something like:
/api/foo/
try with
/api/foo/<id>/
of course this won't be enough. You should also consider a few more things.
You are hooking on perform_update
method. But that's not likely to be correct. perform_update
is called in order to update the whole object with serializer.save()
and therefore it get called only when serializer.is_valid()
.
This means that you will have to send a valid Foo
object. But that's not what you want. You just need to update a single field of the Foo
object. So the right approach here is to use partial_update
. partial_update
will be used when you make a PATCH
request to /api/foo/<id>/
So if you send a PATCH
request to /api/foo/<id>/
with active=0
inside request.data DRF will update the object automatically without any further code changes. Just using
class DisableFoo(generics.UpdateAPIView):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = FooSerializer
queryset = Foo.objects.all()
But this will eventually expose all your model fields to update. So you can override the partial_update
method like so:
def partial_update(self, request, *args, **kwargs):
instance = self.get_object()
instance.active = False
instance.save(update_fields=['active'])
return Response()
DRF support creation of extra extra-actions via @detail_route
and @list_route
decorators.
You can use the @detail_route
to create a custom disable action. Consider the following peace of code:
class FooViewSet(viewsets.GenericViewSet):
queryset = Foo.objects.all()
serializer_class = FooSerializer
@detail_route(methods=['post'])
def disable(self, request, pk=None):
instance = self.get_object()
instance.active = False
instance.save(update_fields=['active'])
return Response('done')
Making a POST
request to /api/foo/<id>/disable
will call the disable
method we just wrote and disable the foo
instance under <id>
.
This way you can escape the requirement to use PATCH
request method.
Upvotes: 1