Bill Noble
Bill Noble

Reputation: 6744

Unable to extend Django REST Framework viewset to handle multi-level API

I am currently using Django REST Framework with routers and viewsets to handle the following API calls:

/movie/
/movie/highlight
/movie/<id>/other

I can't seem to find a way to extend the viewset to handle, in addition to the above, the following API calls:

/movie/<id>
/movie/<id>/other/<num>

My views.py currently looks like this:

from django.contrib.auth.models import User, Group
from movies.models import Movie


from api.serializers import UserSerializer, GroupSerializer, MovieSerializer

from rest_framework.response import Response
from rest_framework import permissions
from rest_framework import renderers
from rest_framework import viewsets
from rest_framework.decorators import list_route, detail_route

class MovieViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows groups to be viewed or edited.
    """
    queryset = Movie.objects.all().order_by('-title')
    serializer_class = MovieSerializer


    # Handle URL: /movie/highlight
    @list_route(renderer_classes=(renderers.StaticHTMLRenderer,))
    def highlight(self, request, *args, **kwargs):
        print "highlight"
        snippet = "Highlight"
        return Response(snippet)

    # Handle URL: /movie/<id>/other
    @detail_route(renderer_classes=(renderers.StaticHTMLRenderer,))
    def other(self, request, *args, **kwargs):
        print "highlight" + self.kwargs['pk']
        snippet = "Highlight" + self.kwargs['pk']
        return Response(snippet)

My urls.py looks like this:

from django.conf.urls import url, include
from rest_framework import routers
from api import views

router = routers.DefaultRouter()
router.register(r'movie', views.MovieViewSet)		

urlpatterns = [
    url(r'^', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

I have searched high and low for an example of how to do this, including all of the DRF documentation, but haven't found anything relevant.

Upvotes: 2

Views: 1677

Answers (1)

DmitrySemenov
DmitrySemenov

Reputation: 10325

You need to use this library to have nested resources https://github.com/alanjds/drf-nested-routers

sample code from one of my projects using this lib

class NestedChatViewSet(
    viewsets.mixins.CreateModelMixin,
    viewsets.mixins.ListModelMixin,
    viewsets.GenericViewSet):

    queryset = Chat.objects.all()
    serializer_class = ChatSerializer
    permission_classes = (IsAuthenticated, IsUserVerified, IsUserSignedPrivacyAgreement, IsUserOfConsent, )

    def get_queryset(self):
        return Chat.objects.filter(consent=self.kwargs['consent_pk']).order_by('-created')

    def get_consent(self, request, pk=None):
        consent = get_object_or_404(Consent, pk=pk)
        self.check_object_permissions(self.request, consent)
        self.consent = consent
        return self.consent

    def create(self, request, *args, **kwargs):
        self.get_consent(request, pk=kwargs['consent_pk'])
        return super().create(request, *args, **kwargs)

    def perform_create(self, serializer):
        serializer.save(
            user=self.request.user,
            consent=self.consent
        )


    def list(self, request, *args, **kwargs):
        self.get_consent(request, pk=kwargs['consent_pk'])
        return super().list(request, *args, **kwargs)

and then in url.py

router = DefaultRouter()
router.register(r'users', PeopleViewSet)
router.register(r'consents', ConsentViewSet)
consents_router = NestedSimpleRouter(router, r'consents', lookup='consent')
consents_router.register(r'chat', NestedChatViewSet)

and now you can get GET, POST consents/ID/chat you can have unlimited amount of nested resources here.

good tutorial is here http://www.machinalis.com/blog/nested-resources-with-django/

Upvotes: 3

Related Questions