Search code examples
pythondjangorestdjango-rest-frameworknested-resources

How to build a nested route with Django REST Framework?


I want to build a simple API and picked up Django REST Framework (DRF). I want to minimize path nesting as Tom Christie (the author of DRF) pointed out to me.

There are two models: Article and Comment.
For these I want to build the following API paths (with create [C], read [R] and update [U] access):

/                      [R]
/articles/             [R]
/articles/:id/         [R]
/articles/:id/comments [R]
/comments/             [CRU]
/comments/:id          [R]

models.py:

class Article(models.Model):
    text = models.CharField(max_length=140, blank=False)
    recorded_at = models.DateTimeField(blank=False)
    modified_at = models.DateTimeField(auto_now_add=True)


class Comment(models.Model):
    message = models.CharField(max_length=140, blank=False)
    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now_add=True)
    article = models.ForeignKey('Article', default=1, related_name='article')

serializers.py:

class ArticleSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Article


class CommentSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Comment

views.py:

class ArticleViewSet(mixins.RetrieveModelMixin,
                     mixins.ListModelMixin,
                     viewsets.GenericViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer


class CommentViewSet(mixins.CreateModelMixin,
                     mixins.RetrieveModelMixin,
                     mixins.ListModelMixin,
                     viewsets.GenericViewSet):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer

urls.py:

router = DefaultRouter()
router.register(r'articles', ArticleViewSet)
router.register(r'comments', CommentViewSet)


urlpatterns = patterns('',
    url(r'^', include(router.urls)),
)

The path at root level work:

/                      [R]
/articles/             [R]
/articles/:id/         [R]
/comments/             [CRU]
/comments/:id          [R]

However the nested path returns 404.

/articles/:id/comments [R]

Solution

  • You'll need something like this on your ArticleViewSet...

    @detail_route(methods=['get'])
    def comments(self, request, pk=None):
        article = self.get_object()
        comments = article.comment_set.all()
        serializer = CommentSerializer(comments)
        return Response(serializer.data)