TAGS :Viewed: 4 - Published at: a few seconds ago

[ Django REST Framework @detail_route not working as expected ]

I can't get @detail_route to work as I think it is supposed to.

I have two API calls I want to handle:

/movie/
/movie/highlight
</div>
</div>
</p>

<p>I am trying to use @detail_route to pickup the /movie/highlight url in the viewset.</p>

<p>My urls.py looks like this:</p>

<p><div class="snippet" data-lang="js" data-hide="false">
<div class="snippet-code">
<pre class="snippet-code-js lang-js prettyprint-override"><code>from django.conf.urls import url, include
from rest_framework import routers
from api import views

router = routers.DefaultRouter()
router.register(r'users', views.UsersViewSet)    
router.register(r'groups', views.GroupViewSet)    
router.register(r'movie', views.MovieViewSet)        

My views.py looks like this:

rom 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 detail_route

# Code from DRF quickstart tutorial
class UsersViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer

# Code from DRF quickstart tutorial
class GroupViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows groups to be viewed or edited.
    """
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

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

    @detail_route(renderer_classes=(renderers.StaticHTMLRenderer,))
    def highlight(self, request, *args, **kwargs):
        snippet = "Highlight"
        return Response(snippet)
</div>
</div>
</p>

<p>The serializers.py looks like this:</p>

<p><div class="snippet" data-lang="js" data-hide="false">
<div class="snippet-code">
<pre class="snippet-code-js lang-js prettyprint-override"><code>from django.contrib.auth.models import User, Group
from movies.models import *
from rest_framework import serializers

class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('url', 'username', 'email', 'groups')


class GroupSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Group
        fields = ('url', 'name')

# My code...
class MovieSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Movie
        fields = ('title', 'details')

If I try the /movie/ url API call it works as expected. If I try the /movie/highlight/ call I get a 404 error.

I am a newbie to DRF so suspect I am doing something very silly here but can't find out what from the various docs and tutorials I have scanned.

Answer 1


You need to use @list_route decorator instead of @detail_route decorator.

This will generate the url movie/highlight/.

Using @list_route decorated method generates the url of type {prefix}/{methodname}/ whereas detail_route decorated method generateds url of type {prefix}/{lookup}/{methodname}/. Here methodname is name of your method and lookup is the lookup value on which lookup is performed to get the object for detail view.

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

    # use list_route decorator
    @list_route(renderer_classes=(renderers.StaticHTMLRenderer,))
    def highlight(self, request, *args, **kwargs):
        snippet = "Highlight"
        return Response(snippet)

Answer 2


If you register your URLs using routers.DefaultRouter(), it will generate the URL /movie/pk/highlight instead of /movie/highlight. If you want a custom URL other than the pre-generated one, use URL mapping.