Django: Rest Framework Views Cheat Sheet

There are many options for creating views for your web API, it really depends on what you want and personal preference.

Using Function-based views:

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.parsers import JSONParser
from rest_framework import status
from posts.models import Post
from posts.serializers import PostSerializer

@api_view(['GET', 'POST'])
def post_list(request, format=None):

    if request.method == 'GET':
        posts = Post.objects.all()
        serializer = PostSerializer(posts, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = PostSerializer(data=data)

        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Using Class-based views:

from rest_framework.response import Response
from rest_framework import status
from rest_framework.views import APIView
from posts.models import Post
from posts.serializers import PostSerializer


class PostList(APIView):
    def get(self, request, format=None):
        snippets = Post.objects.all()
        serializer = PostSerializer(snippets, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = PostSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Using Generic Class-based views:

from rest_framework import generics
from posts.models import Post
from posts.serializers import PostSerializer


class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

Using Mixins:

from rest_framework import generics, mixins
from posts.models import Post
from posts.serializers import PostSerializer


class PostList(generics.GenericAPIView,
               mixins.ListModelMixin,
               mixins.CreateModelMixin
               ):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

Using ViewSets:

With ModelViewSet (in this case), you don’t have to create separate views for getting list of objects and detail of one object. ViewSet will handle it for you in a consistent way for both methods.

from rest_framework import viewsets
from posts.models import Post
from posts.serializers import PostSerializer


class PostViewSet(viewsets.ModelViewSet):
    """
    A viewset for viewing and editing post instances.
    """
    queryset = Post.objects.all()
    serializer_class = PostSerializer

Routers

Routers in ViewSets allow the URL configuration for your API to be automatically generated using naming standards.

from rest_framework.routers import DefaultRouter
from posts.views import PostViewSet

router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = router.urls

Custom Actions in ViewSets

DRF provides helpers to add custom actions for ad-hoc behaviours with the @action decorator. The router will configure its url accordingly. For example, we can add a comments action in the our PostViewSet to retrieve all the comments of a specific post as follows:

from rest_framework import viewsets
from rest_framework.decorators import action
from posts.models import Post
from posts.serializers import PostSerializer, CommentSerializer


class PostViewSet(viewsets.ModelViewSet):
   ...
   
   @action(methods=['get'], detail=True)
   def comments(self, request, pk=None):
       try:
           post = Post.objects.get(id=pk)
       except Post.DoesNotExist:
           return Response({"error": "Post not found."},
                           status=status.HTTP_400_BAD_REQUEST)
       comments = post.comments.all()
       return Response(CommentSerializer(comments, many=True))

Upon registering the view as router.register(r'posts', PostViewSet), this action will then be available at the url posts/{pk}/comments/.

Leave a Reply

Your email address will not be published. Required fields are marked *