Django REST Framework API Views

Building API endpoints with different view types

👁️ What are API Views?

API Views are the core of DRF endpoints. They handle HTTP requests, process data using serializers, and return responses. DRF offers multiple view types from simple function-based to powerful class-based views.


# Simple function-based view
from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view(['GET'])
def hello_world(request):
    return Response({'message': 'Hello, World!'})
                                    

Output:

{
    "message": "Hello, World!"
}

View Types

Function Views

Simple decorator-based views

@api_view(['GET', 'POST'])
def book_list(request):
    # Handle request
    pass
🎯

APIView

Class-based view foundation

class BookList(APIView):
    def get(self, request):
        return Response(data)
🔧

Generic Views

Pre-built common patterns

class BookList(ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
🚀

Mixins

Reusable view behaviors

class BookList(ListModelMixin, 
               GenericAPIView):
    pass

🔹 Function-Based Views

The simplest way to create API endpoints using the @api_view decorator. Perfect for beginners and simple endpoints with straightforward logic:

# views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import Book
from .serializers import BookSerializer

@api_view(['GET', 'POST'])
def book_list(request):
    """
    List all books or create a new book
    """
    if request.method == 'GET':
        books = Book.objects.all()
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)
    
    elif request.method == 'POST':
        serializer = BookSerializer(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)

@api_view(['GET', 'PUT', 'DELETE'])
def book_detail(request, pk):
    """
    Retrieve, update or delete a book
    """
    try:
        book = Book.objects.get(pk=pk)
    except Book.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)
    
    if request.method == 'GET':
        serializer = BookSerializer(book)
        return Response(serializer.data)
    
    elif request.method == 'PUT':
        serializer = BookSerializer(book, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    elif request.method == 'DELETE':
        book.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

🔹 APIView Class

Class-based views provide better organization and code reuse. Each HTTP method becomes a separate class method for cleaner structure:

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Book
from .serializers import BookSerializer

class BookList(APIView):
    """
    List all books or create a new book
    """
    def get(self, request):
        books = Book.objects.all()
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)
    
    def post(self, request):
        serializer = BookSerializer(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)

class BookDetail(APIView):
    """
    Retrieve, update or delete a book
    """
    def get_object(self, pk):
        try:
            return Book.objects.get(pk=pk)
        except Book.DoesNotExist:
            return None
    
    def get(self, request, pk):
        book = self.get_object(pk)
        if book is None:
            return Response(status=status.HTTP_404_NOT_FOUND)
        serializer = BookSerializer(book)
        return Response(serializer.data)
    
    def put(self, request, pk):
        book = self.get_object(pk)
        if book is None:
            return Response(status=status.HTTP_404_NOT_FOUND)
        serializer = BookSerializer(book, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    def delete(self, request, pk):
        book = self.get_object(pk)
        if book is None:
            return Response(status=status.HTTP_404_NOT_FOUND)
        book.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
# urls.py
from django.urls import path
from .views import BookList, BookDetail

urlpatterns = [
    path('books/', BookList.as_view(), name='book-list'),
    path('books//', BookDetail.as_view(), name='book-detail'),
]

🔹 Generic Views

Generic views eliminate boilerplate code by providing pre-built implementations for common patterns. Just set queryset and serializer_class:

# views.py
from rest_framework import generics
from .models import Book
from .serializers import BookSerializer

# List and Create
class BookList(generics.ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

# Retrieve, Update, and Delete
class BookDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

Available Generic Views:

  • ListAPIView: Read-only list endpoint
  • CreateAPIView: Create-only endpoint
  • RetrieveAPIView: Read-only single object
  • UpdateAPIView: Update-only endpoint
  • DestroyAPIView: Delete-only endpoint
  • ListCreateAPIView: List and create
  • RetrieveUpdateAPIView: Read and update
  • RetrieveDestroyAPIView: Read and delete
  • RetrieveUpdateDestroyAPIView: Read, update, delete

🔹 Mixins

Mixins provide reusable behaviors that you can combine to create custom views with specific functionality:

# views.py
from rest_framework import mixins, generics
from .models import Book
from .serializers import BookSerializer

class BookList(mixins.ListModelMixin,
               mixins.CreateModelMixin,
               generics.GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)
    
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

class BookDetail(mixins.RetrieveModelMixin,
                 mixins.UpdateModelMixin,
                 mixins.DestroyModelMixin,
                 generics.GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)
    
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)
    
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

Available Mixins:

  • ListModelMixin: List queryset
  • CreateModelMixin: Create model instance
  • RetrieveModelMixin: Retrieve single instance
  • UpdateModelMixin: Update instance
  • DestroyModelMixin: Delete instance

🔹 Customizing Views

Override methods to add custom behavior to your views:

class BookList(generics.ListCreateAPIView):
    serializer_class = BookSerializer
    
    # Custom queryset
    def get_queryset(self):
        queryset = Book.objects.all()
        # Filter by query parameter
        author = self.request.query_params.get('author')
        if author:
            queryset = queryset.filter(author__icontains=author)
        return queryset
    
    # Custom create behavior
    def perform_create(self, serializer):
        # Add current user as owner
        serializer.save(owner=self.request.user)

Example Request:

GET /api/books/?author=John

Response:
[
    {
        "id": 1,
        "title": "Python Guide",
        "author": "John Doe"
    }
]

🧠 Test Your Knowledge

Which decorator is used for function-based API views?