Support Our Site

To ensure we can continue delivering content and maintaining a free platform for all users, we kindly request that you disable your adblocker. Your contribution greatly supports our site's growth and development.

Django Rest Framework CheetSheet: Mastering API Development

5 min read

Django Rest Framework (DRF) is a powerful toolkit that makes building robust and scalable web APIs with Django a breeze. Whether you're a seasoned Django developer or a newcomer, having a comprehensive cheat sheet at your disposal can be a game-changer. In this article, we'll delve into the most comprehensive Django Rest Framework cheat sheet, covering essential concepts, serializers, views, authentication, and more.

Serializers

Serializers play a pivotal role in converting complex data types, such as Django models, into Python data types that can be easily rendered into JSON, XML, or other content types. Here's a quick guide to DRF serializers:

  • Define a serializer class by inheriting from serializers.Serializer or serializers.ModelSerializer (for model-based serializers).
  • Specify fields using class attributes like CharField, IntegerField, etc.
  • Implement validation logic using methods like validate_<field_name>().
  • Use serializers to handle both input data validation and output data rendering.

class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel
        fields = '__all__'

Basic View Types

Views in DRF are analogous to Django's views, but tailored specifically for handling API requests. They're responsible for processing incoming requests and returning appropriate responses.

DRF provides different types of views to handle various use cases:

  • APIView: The base class for all views. It provides basic request/response handling.
  • ViewSet: Combines multiple views (list, create, retrieve, update, delete) into a single class.
  • GenericAPIView: Provides common behavior for CRUD operations.
  • ModelViewSet: A combination of GenericAPIView and ViewSet tailored for model-backed APIs.

HTTP Methods and Corresponding Views

DRF maps HTTP methods to view methods:

  • GET: list(), retrieve()
  • POST: create()
  • PUT: update()
  • PATCH: partial_update()
  • DELETE: destroy()

Authentication and Permissions

DRF provides authentication and permission classes to control access to views:

from rest_framework.authentication import BasicAuthentication
from rest_framework.permissions import IsAuthenticated

class MyView(APIView):
    authentication_classes = [BasicAuthentication]
    permission_classes = [IsAuthenticated]

Custom Permissions

Define custom permissions to control access:

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        return obj.owner == request.user

ViewSets and Routers

DRF offers ViewSets for a more concise way of defining views:

from rest_framework import viewsets

class MyModelViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

ViewSets can be registered with routers to generate URLs automatically:

from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'mymodels', MyModelViewSet)
urlpatterns += router.urls

Pagination

DRF offers built-in pagination classes for handling large data sets:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
}

Filtering and Ordering

Filter and order querysets using URL query parameters:

class MyModelViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    search_fields = ['name']
    ordering_fields = ['name']

API Versioning

You can version your API to avoid breaking changes:

urlpatterns = [
    path('v1/', include('myapp.urls')),  # Use API versioning in URLs
]

Versioning

Version your APIs using DRF's versioning classes:

REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
    'ALLOWED_VERSIONS': ['v1', 'v2'],
}

Throttling and Rate Limiting

Protect your API using throttling and rate limiting:

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day',
    },
}

Content Negotiation

DRF supports content negotiation for handling different media types (JSON, XML, etc.):

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.XMLRenderer',
    ],
}

Exception Handling

DRF provides built-in exception handling and error responses:

from rest_framework.views import exception_handler

def custom_exception_handler(exc, context):
    response = exception_handler(exc, context)
    if response is not None:
        response.data['custom_message'] = 'An error occurred'
    return response

Overriding Generic Views

You can customize the behavior of generic views by overriding methods:

class MyModelListCreateView(generics.ListCreateAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

View Function Decorators

Use decorators to add behavior to views, such as authentication and permission checks:

from rest_framework.decorators import authentication_classes, permission_classes

@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def my_view(request):
    # View logic here

Serializer Context

Pass additional context to serializers:

serializer = MyModelSerializer(instance, context={'request': request})

Rendering Custom Data

Render custom data using Response and status:

from rest_framework.response import Response
from rest_framework import status

class MyView(APIView):
    def get(self, request):
        data = {'message': 'Hello, world!'}
        return Response(data, status=status.HTTP_200_OK)

File Uploads

Handle file uploads in views using serializers:

class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel
        fields = ['file']

class MyModelView(APIView):
    def post(self, request):
        serializer = MyModelSerializer(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)

Testing Views

DRF provides testing tools to test views and API functionality:

from rest_framework.test import APITestCase

class MyViewTest(APITestCase):
    def test_my_view(self):
        response = self.client.get('/my-view/')
        self.assertEqual(response.status_code, status.HTTP_200_OK)

Serializer Validation

Add custom validation to serializers using validate_<field_name> methods:

class MySerializer(serializers.ModelSerializer):
    def validate_my_field(self, value):
        if value < 0:
            raise serializers.ValidationError('Value cannot be negative')
        return value

DRF with Function-Based Views

You can use DRF features with function-based views:

from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view(['GET'])
def my_function_view(request):
    data = {'message': 'Hello, function view!'}
    return Response(data)

Serializing Relationships

Handle related data using serializers:

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = ['name', 'books']

class BookSerializer(serializers.ModelSerializer):
    author = AuthorSerializer()
    class Meta:
        model = Book
        fields = ['title', 'author']

Combining Views

Combine multiple views into one using ViewSets and mixins:

from rest_framework import mixins, viewsets

class MyViewSet(mixins.ListModelMixin,
                mixins.RetrieveModelMixin,
                viewsets.GenericViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

Caching

Cache responses using DRF's caching decorators:

from rest_framework.decorators import cache_page

class MyView(APIView):
    @cache_page(60 * 15)  # Cache for 15 minutes
    def get(self, request):
        # ...

DRF's Mixins

Leverage DRF's mixins for common CRUD operations:

from rest_framework import mixins, viewsets

class MyViewSet(mixins.ListModelMixin,
                mixins.CreateModelMixin,
                mixins.RetrieveModelMixin,
                mixins.UpdateModelMixin,
                mixins.DestroyModelMixin,
                viewsets.GenericViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MySerializer

Custom Action

Create custom actions in viewsets:

class MyViewSet(viewsets.ModelViewSet):
    @action(detail=True, methods=['post'])
    def do_something(self, request, pk=None):
        # Custom action logic

Query Parameters

Retrieve query parameters in views:

class MyView(APIView):
    def get(self, request):
        param_value = request.query_params.get('param_name')
        # ...

Custom Nested Relationships

You can use serializer methods to create custom nested relationships:

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = '__all__'

class BookSerializer(serializers.ModelSerializer):
    author_data = serializers.SerializerMethodField()

    class Meta:
        model = Book
        fields = '__all__'

    def get_author_data(self, obj):
        author = obj.author
        return AuthorSerializer(author, context=self.context).data

Multiple Serializers for a Single View

Use different serializers for different request methods:

class MyModelAPIView(APIView):
    serializer_class_read = MyModelReadSerializer
    serializer_class_write = MyModelWriteSerializer

    def get_serializer_class(self):
        if self.request.method in ['GET', 'HEAD']:
            return self.serializer_class_read
        return self.serializer_class_write

    def get(self, request):
        # ...
    
    def post(self, request):
        # ...

Override perform_create in your view to handle related data creation:

class OrderCreateView(generics.CreateAPIView):
    serializer_class = OrderSerializer

    def perform_create(self, serializer):
        order = serializer.save()
        products_data = self.request.data.get('products')
        if products_data:
            for product_data in products_data:
                product = Product.objects.get(id=product_data['id'])
                OrderItem.objects.create(order=order, product=product, quantity=product_data['quantity'])

Django Rest Framework provides a wide array of tools and features to simplify and streamline API development. With this cheatsheet, you have a comprehensive reference to help you navigate the complexities of DRF views and create efficient and robust APIs for your Django applications.


DJANGO

Latest Articles

Latest from djangocentral

How to Use Subquery() in Django With Practical Examples

In the realm of web development, Django stands as a powerful and versatile framework for building robust applications. One of the key aspects of developing efficient and optimized web applications is handling database queries effectively. In this article…
Read more →

4 min read

DRF Serializer: Handling OrderedDict and Converting It to a Dictionary or JSON

In Django Rest Framework (DRF) tests, when you access serializer.data, you might encounter an OrderedDict instead of a regular dictionary. This behavior is intentional and reflects the design of DRF's serialization process.Understanding the Problem The u…
Read more →

3 min read

How to Perform NOT Queries in Django ORM

In Django, performing NOT queries allows you to exclude certain records from the query results based on specific conditions. The NOT operator, represented by the tilde (~) when used in conjunction with the Django ORM's Q object, helps you construct compl…
Read more →

3 min read

How to Perform OR Queries in Django ORM

Django is a popular web framework for Python that provides an intuitive and powerful Object-Relational Mapping (ORM) system. The Django ORM allows developers to interact with databases using Python classes and methods, abstracting away the underlying SQL…
Read more →

3 min read