There are many options for creating views for your web API, it really depends on what you want and personal preference.
Table of Contents
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/.