Possible to have a method on an APIView called from a url
Solution 1
Both of the answers posted here by @knbk and @almalki are both valid approaches to what I was asking. However they are not showing what I actually ended up doing after a few hours or so of looking into it.
I ended up using ViewSets that allowed me to bind the GET
, POST
, etc requests to a certain function in a ViewSet class. Usually you would use routers to bind all of the appropriate functions automatically, but I wanted a little more flexibility with how they were bound so I just write them out myself.
# views.py
class CartViewSet(ViewSet):
def clear(self, request):
"""Clear the users cart."""
queryset = Cart.objects.get(user=request.user)
queryset.clear_cart()
serializer = CartSerializer(queryset)
return Response(serializer.data, status=status.HTTP_200_OK)
clear_cart_viewset = CartViewSet.as_view({
'post': 'clear'
})
# urls.py
urlpatterns = patterns('app.views',
....
url(r'^cart/clear/$', 'clear_cart_viewset', name='clear_cart_api'),
....
)
Solution 2
You seem to miss the point of how a class-based view's flow works.
- As a class-based view is a class (obviously), and Django expects an
unbound function (not attached to a class or instance) as a view,
as_view
handles that by creating an unbound function, and in that function instantiating the class-based view. -
as_view
then callsself.dispatch(request, *args, **kwargs)
, whereself
is the instantiated object that's just created. -
dispatch
calls eitherself.get(request, *args, **kwargs)
orself.post(request, *args, **kwargs)
, depending on the request method (orput
,patch
ordelete
if those are allowed and used).
There's no room for a custom function like your clear
function, unless you override one of these methods to call self.clear(request)
. The equivalent of @api_view(['POST'])
would be to override the post(request, *args, **kwargs)
method:
# views.py
class CartAPIView(APIView):
def post(self, request, *args, **kwargs):
# Why would you call this 'queryset'? It's a single object.
cart = Cart.objects.get(user=request.user)
cart.clear_cart()
serializer = CartSerializer(cart)
return Response(serializer.data, status=status.HTTP_200_OK)
# urls.py
urlpatterns = patterns('app.views',
url(r'^cart/clear/$', CartAPIView.as_view(), name='clear_cart_api'),
)
Solution 3
It seems what you are looking for is Function Based Views, where you can decorate a function with @api_view()
from rest_framework.decorators import api_view
@api_view(['POST'])
def clear(request):
"""Clear the users cart."""
queryset = Cart.objects.get(user=request.user)
queryset.clear_cart()
serializer = CartSerializer(queryset)
return Response(serializer.data, status=status.HTTP_200_OK)
# urls.py
urlpatterns = patterns('app.views',
....
url(r'^cart/clear/$', 'clear', name='clear_cart_api'),
....
)
JDWardle
Updated on July 09, 2022Comments
-
JDWardle almost 2 years
In Django Rest Framework is it possible to have a custom method in an
APIView
class be called similar to how.get()
or.post()
would be called.I know it's possible with routers using the
@action()
or@link()
decorators, I'm trying to figure out if you can do something similar to anAPIView
but have it so I can set the method to whatever url I want.I've tried decorating the class with
@action()
and@api_view()
but nothing seems to have worked.I'm not exactly sure what I should put in the url for an endpoint to actually call the method in the class. Would I use
CartAPIView.clear.as_view()
,CartAPIView.clear
, orCartAPIView.clear()
. I've tried different combinations of calls toCartAPIView
but nothing has worked.Here's an example of what I'm trying to do:
# views.py class CartAPIView(APIView): @api_view(['POST']) def clear(self, request): """Clear the users cart.""" queryset = Cart.objects.get(user=request.user) queryset.clear_cart() serializer = CartSerializer(queryset) return Response(serializer.data, status=status.HTTP_200_OK) # urls.py urlpatterns = patterns('app.views', .... url(r'^cart/clear/$', CartAPIView.clear.as_view(), name='clear_cart_api'), .... )
Any help would be appreciated.