Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible to have a method on an APIView called from a url

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 an APIView 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, or CartAPIView.clear(). I've tried different combinations of calls to CartAPIView 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.

like image 242
JDWardle Avatar asked Mar 20 '14 23:03

JDWardle


2 Answers

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 calls self.dispatch(request, *args, **kwargs), where self is the instantiated object that's just created.
  • dispatch calls either self.get(request, *args, **kwargs) or self.post(request, *args, **kwargs), depending on the request method (or put, patch or delete 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'),
)
like image 112
knbk Avatar answered Nov 11 '22 14:11

knbk


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'),
    ....
)
like image 38
almalki Avatar answered Nov 11 '22 15:11

almalki