Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested resources in Django REST Framework

I wish to implement my new API with a nested resource.

Example: /api/users/:user_id/posts/

Will evaluate to all of the posts for a specific user. I haven't seen an working example for this use case, maybe this isn't the right way for implementing rest API?

like image 840
Tal Peretz Avatar asked Oct 20 '14 04:10

Tal Peretz


People also ask

What is nested serializer in Django REST framework?

DRF provides a Serializer class that gives you a powerful, generic way to control the output of your responses, as well as a ModelSerializer class that provides a useful shortcut for creating serializers that deal with model instances and querysets.

What nested resources?

Nesting resources provide REST API consumers an easy and efficient way to manage data by allowing the consumer to send and receive only the required object. The nested resource must be a business object, that is, it must still represent a complete business object.

What is Basename in Django REST framework?

basename - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the queryset attribute of the viewset, if it has one. Note that if the viewset does not include a queryset attribute then you must set basename when registering the viewset.


3 Answers

As commented by Danilo, the @link decorator got removed in favor of @list_route and @detail_route decorators.

Update: @detail_route & @list_route got deprecated in favor of @action.

Here's the alternate solutions:

Solution 1:

@detail_route()
def posts(self, request, pk=None):
    owner = self.get_object()
    posts = Post.objects.filter(owner=owner)

    context = {
        'request': request
    }

    post_serializer = PostSerializer(posts, many=True, context=context)
    return Response(post_serializer.data)

Solution 2:

Try drf-nested-routers. Haven't tried this out yet, but looks promising, many are already using it. Looks like an advanced version of what we are already trying to achieve.

Hope this helps.

like image 132
Ankit Popli Avatar answered Oct 13 '22 18:10

Ankit Popli


To map /api/users/:user_id/posts/ you can decorate a posts method inside your ViewSet with @link()

from rest_framework.decorators import link
from rest_framework.response import Response


class UserViewSet(viewsets.ModelViewSet):
    model = User
    serializer_class = UserSerializer
    
    # Your regular ModelViewSet things here

    # Add a decorated method like this
    @link()
    def posts(self, request, pk):
        # pk is the user_id in your example
            
        posts = Post.objects.filter(owner=pk)    
        
        # Or, you can also do a related objects query, something like:
        # user = self.get_object(pk)
        # posts = user.post_set.all()

        # Then just serialize and return it!
        serializer = PostSerializer(posts)
        return Response(serializer.data)

    
like image 31
bakkal Avatar answered Oct 13 '22 19:10

bakkal


As commented by Danilo Cabello earlier you would use @detail_route or @list_route instead of @link(). Please read the documentation for "Routers", section "Extra link and actions" and "ViewSets", section "Marking extra actions for routing" for detailed explanations.

like image 27
JJD Avatar answered Oct 13 '22 17:10

JJD