Django REST Framework Views
Understanding API View Creation in Django REST Framework
There are many options for creating views for your API, it really depends on your API's requirements or personal preference.
We'll write a post_list
view with GET
and POST
methods for reading and creating new Post instances.
Function-Based Views for APIs
Function-based views offer a straightforward way to handle API requests. They are defined as Python functions that accept a request object and return a response object.
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)
Class-Based Views for APIs
Class-based views provide a more object-oriented approach to building APIs, allowing for better organization and reusability of code.
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)
Leveraging Generic Class-Based Views
Django REST Framework offers a set of generic views that abstract common patterns, reducing boilerplate code significantly.
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
Building APIs with Mixins
Mixins provide specific actions that can be combined with generic views to create more specialized functionality.
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)
Efficient API Management with ViewSets
ViewSets consolidate logic for multiple related views into a single class, simplifying URL routing and code management.
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
So basically, this would not only generate the list
view, but also the detail
view for every Post instance.
Routers for Automatic URL Configuration
Routers in ViewSets allow the URL configuration for your API to be automatically generated using naming standards, which is highly efficient.
from rest_framework.routers import DefaultRouter
from posts.views import PostViewSet
router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = router.urls
Adding Custom Actions to 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):
queryset = Post.objects.all()
serializer_class = PostSerializer
@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/
.