Is there an easy way to call an existing ViewSet retrieve call from another viewset?

5,817 views
Skip to first unread message

David Kaplan

unread,
Jun 29, 2015, 9:12:53 PM6/29/15
to django-res...@googlegroups.com
I would like to call an existing ViewSet that I have created from a new ViewSet. The existing viewset I want to call handles all of my User data updates and retrievals. The new viewset takes a userid as a parameter in order to look up related objects. I would like to reuse the existing viewset because it already encapsulates my permission logic, filter logic, and custom retrieval logic for users and I want to use this same validation as a first step for this new call. I have been researching, but have not seen a solution posted.

The 2 problems I see is that viewset functions like retrieve take in a Request and return a Response. I could create and manually pack a request and manually unpack a response within my new viewset, but I feel icky just writing that.

Is there an easier more standard way to reuse viewsets as internal APIs?

Filipe Ximenes

unread,
Jun 30, 2015, 12:30:11 PM6/30/15
to django-res...@googlegroups.com
Hi David, pardon if I din't understand the question but wouldn't creating a mixin to be shared by the 2 ViewSets solve this? This way you would have a separate class with the code that can be used by the ViewSets and extend it in each one of then.

--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-fram...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
  
Filipe Ximenes
+55 (81) 8245-9204
Vinta Software Studio
http://www.vinta.com.br

David Kaplan

unread,
Jul 1, 2015, 10:08:48 AM7/1/15
to django-res...@googlegroups.com
Hi Filipe,
I don't think that would work if I am understanding the suggestion.
Let me try to clarify:

I have a userprofile modelviewset that looks something like this:
class UserProfileViewSet(viewsets.ModelViewSet):
    permission_classes
=(CustomPermissionClass, )
    queryset
= UserProfile.objects.all()
    serializer_class
= UserProfileSerializer

class ProjectViewSet(viewset.ModelViewSet):
    permission_classes
= (AnotherCustomPermissionClass,)
    queryset
=Project.objects.all()
    serializer_class
= ProjectSerializer

   
@list_route(methods=['get'])
   
def forprofile(request, *args, **kwargs):
        user_id
= request.query_params.get('user_id', None)

       
#This is where I would like to be able to use the retrieve functionality that django rest framework provides for free to lookup the user and save it to a "user" variable

        projects
= self.get_queryset().filter(user=user)
        serializer
= self.get_serializer(projects, many=True)
       
return Response(serializer.data)


So if you look at the above. I would like to be able to use the existing retrieve function that is automatically defined on the UserProfileViewSet so that I can take advantage of django rest framework and the permissions I have defined for that class.
I can create a mixin for the UserProfileViewSet that can define the permission_classes, serializer_class and queryset. That mixin would work for other UserProfileViewsets, but would not work for a viewset for a completely different asset (like project), because those assets also need their own definition of queryset and permission_classes.
Let me know if I misunderstood.



On Tuesday, June 30, 2015 at 12:30:11 PM UTC-4, Filipe Ximenes wrote:
Hi David, pardon if I din't understand the question but wouldn't creating a mixin to be shared by the 2 ViewSets solve this? This way you would have a separate class with the code that can be used by the ViewSets and extend it in each one of then.
On Mon, Jun 29, 2015 at 10:12 PM, David Kaplan <dave.b...@gmail.com> wrote:
I would like to call an existing ViewSet that I have created from a new ViewSet. The existing viewset I want to call handles all of my User data updates and retrievals. The new viewset takes a userid as a parameter in order to look up related objects. I would like to reuse the existing viewset because it already encapsulates my permission logic, filter logic, and custom retrieval logic for users and I want to use this same validation as a first step for this new call. I have been researching, but have not seen a solution posted.

The 2 problems I see is that viewset functions like retrieve take in a Request and return a Response. I could create and manually pack a request and manually unpack a response within my new viewset, but I feel icky just writing that.

Is there an easier more standard way to reuse viewsets as internal APIs?

--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+unsub...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Filipe Ximenes

unread,
Jul 2, 2015, 8:38:51 AM7/2/15
to django-res...@googlegroups.com
Yep, you are right.

Again, not sure I got 100%, but see if this works:
I didn't test it so I may have forgotten something but I hope you can get the idea. Also, not the most beautiful solution.
from rest_framework import permissions

class CustomViewPermission(permissions.BasePermission):

    def has_permission(self, request, view):
        if hasattr(view, 'custom_permission'):
            return view.custom_permission(request)
        return True

    def has_object_permission(self, request, view, obj):
        if hasattr(view, 'custom_object_permission'):
            return view.custom_object_permission(request, obj)
        return True

class UserProcessMixin(object):

    def custom_object_permission(request, obj):
        # do your permission checking here
        # using self.user NOT obj
        return True # or False depending on your checking

class UserProfileViewSet(UserProcessMixin, viewsets.ModelViewSet):
    permission_classes =(CustomViewPermission, )
    queryset = UserProfile.objects.all()
    serializer_class = UserProfileSerializer

    def get_object(self):
        self.user = super(UserProfileViewSet, self).get_object()
        return self.user

class ProjectViewSet(UserProcessMixin, viewset.ModelViewSet):
    permission_classes = (CustomViewPermission,)
    queryset =Project.objects.all()
    serializer_class = ProjectSerializer

    def get_user(self, user_id):
        self.user = User.objects.get(id=user_id)
        return self.user

    @list_route(methods=['get'])
    def forprofile(request, *args, **kwargs):
        user_id = request.query_params.get('user_id', None)

        self.get_user(user_id)
        self.check_object_permissions(self.request, None)

        projects = self.get_queryset().filter(user=user)
        serializer = self.get_serializer(projects, many=True)
        return Response(serializer.data)


To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-fram...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
  
Filipe Ximenes
+55 (81) 8245-9204
Vinta Software Studio
http://www.vinta.com.br

--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-fram...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

David Kaplan

unread,
Jul 2, 2015, 8:56:52 AM7/2/15
to django-res...@googlegroups.com
Thanks for the help Filipe. This is what I expected. I was hoping to be able to reuse the view so that I don't have to duplicate its logic in other apis, but it does not look like that is an option. I will have to do something like what you suggested where I access the model directly. The reason I was hoping to use the UserViewSet is so that if I make a change there it will apply to everything that uses it.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+unsub...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
  
Filipe Ximenes
+55 (81) 8245-9204
Vinta Software Studio
http://www.vinta.com.br

--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+unsub...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Rocky Meza

unread,
Jul 2, 2015, 8:45:14 PM7/2/15
to django-res...@googlegroups.com
I've always liked the idea of calling my API from within Python, for reasons of DRYness and dogfooding.

I was able to do it like this:

def another_view(request, *args, **kwargs):
    viewfn
= MyViewSet.as_view({'get': 'retrieve'})
    response
= viewfn(request, *args, **kwargs)
   
# directly use response.data

It's been a while since I wrote it, but I think you get the idea.

Eventually I was hoping to have a requests-like library that could be used for calling the API, then if I ever had to separate the app server from the API server, I could just switch to requests.
Reply all
Reply to author
Forward
0 new messages