Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call ViewSet method from another view

I have a mobile app with a Django REST framework API and I have a lot of ModelViewSet that I call to retrieve data. I have performance issue because I need to call a lot of routes after the user login, and I would like to keep the REST logic but also returns after the login all the viewsets content in the login response (keeping their filters).

Is it possible to call a ModelViewset list() from another view (viewset or APIView)?

The only answer I found on SO was to do something like this

class ContentGenerator(APIView):

    def get(self, request, format=None):
         data = MyModelViewSet.as_view({'get': 'list'})(request).data

         return Response({'data': data})

But it is not supported

Exception Value: The `request` argument must be an instance of `django.http.HttpRequest`, not `rest_framework.request.Request`

Is there another solution?

like image 648
Florian Thuin Avatar asked Jul 03 '18 08:07

Florian Thuin


People also ask

How do I view all extra actions in a viewset?

To view all extra actions, call the .get_extra_actions () method. Extra actions can map additional HTTP methods to separate ViewSet methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments.

What is a viewset class in Java?

A ViewSet class is simply a type of class-based View, that does not provide any method handlers such as .get () or .post (), and instead provides actions such as .list () and .create (). The method handlers for a ViewSet are only bound to the corresponding actions at the point of finalizing the view, using the .as_view () method.

How do I register a viewset in the urlconf?

The method handlers for a ViewSet are only bound to the corresponding actions at the point of finalizing the view, using the .as_view () method. Typically, rather than explicitly registering the views in a viewset in the urlconf, you'll register the viewset with a router class, that automatically determines the urlconf for you.

What are the attributes of a viewset?

suffix - the display suffix for the viewset type - mirrors the detail attribute. name - the display name for the viewset. This argument is mutually exclusive to suffix. description - the display description for the individual view of a viewset. You may inspect these attributes to adjust behaviour based on the current action.


2 Answers

After some research, it looks like there might be side-effects to reuse the request in other views, so it is not officially supported by DRF. It is hacky, but if you know what you are doing, this is a solution.

data = MyModelViewSet.as_view({'get': 'list'})(request._request).data
like image 106
Florian Thuin Avatar answered Oct 12 '22 23:10

Florian Thuin


I solved this in 2 ways. Assume I have a view A and a viewset B and that I want to call B.my_action from A. B.my_action handles a specific id e.g. user id, product id etc.

1st way

code w/in BViewSet

@action(detail=True, methods=['post'])
def my_action(self, request, pk=None):
 try:
   # works when you call from web UI, through router
   # automatically you have one specific view corresponding
   # to specific id e.g. user id, product id etc
   instance = self.get_object()
 except AttributeError as ae:
   # here you don't have a specific view instantiated
   # and you have many ids e.g. user ids, product ids etc
   # pick one id
   instance = MyModelObject.objects.get(id=pk)

 # rest of code as is, no change

code w/in e.g. AView.utils.py that calls BViewSet.my_action

Below request is the request passed to AView when AView called through web UI.

from django.http import HttpRequest

data = BViewSet(detail=True)
new_request = HttpRequest()
new_request.data = any_data_I_need_to_pass # e.g. request.data

ret = data.my_action(new_request, pk=request.data['id'])
return ret

2nd way

I decoupled my_action functionality from my_action call i.e.

code w/in BViewSet

@action(detail=True, methods=['post'])
def my_action(self, request, pk=None):
  instance = self.get_object()
  ret = my_action_server(instance, request.data)
  return Response(data=ret)

def my_action_server(instance, data):
   # all code comes here
   return result

code w/in Aview

# import my_action_server as needed
# call my_action_server
# AView should provide everything needed
like image 38
vpap Avatar answered Oct 12 '22 21:10

vpap