Django Rest Framework CheetSheet
Django Rest Framework CheetSheet

Django REST Framework (DRF) CheatSheet

The Django Rest Framework (DRF) is a powerful toolkit that makes it easy to create robust and scalable web APIs with Django. Whether you’re an experienced Django developer or a beginner, having a comprehensive cheat sheet at your disposal can be a game changer. In this article, we’ll go over the most comprehensive Django Rest Framework cheat sheet, which includes key concepts, serializers, views, authentication, and more.

Serializers

Serializers play an important 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 formats. Here’s a quick guide for DRF serializers:

  • Define a serializer class by inheriting from serializers.Serializer or serializers.ModelSerializer (for model-based serializers).
  • Specify fields using class attributes like CharFieldIntegerField, 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:

  • GETlist()retrieve()
  • POSTcreate()
  • PUTupdate()
  • PATCHpartial_update()
  • DELETEdestroy()

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 offers a variety of tools and features to help you simplify and streamline API development. This cheatsheet provides a comprehensive reference to help you navigate the complexities of DRF views and create efficient and robust APIs for your Django applications.

Recommended Read: Django for APIs

If you’re serious about building APIs with Django, I highly recommend checking out the book [Django for APIs: Build web APIs with Python and Django]

A Note From the Author

Thank you so much for taking the time to read the story. If you found my article helpful and interesting, please share your thoughts in the comment section, and don’t forget to share and clap

Let’s Get in Touch!

Show 7 Comments

7 Comments

  1. Tom

    Related to authentication and permissions. I have used something similar to this in my viewsets which allow you to use Django default object permissions similar to how the Django admin uses them (it is only really adding the get permissions but is an easy way to restrict an API) Than you can use the default Django permissions with groups to easily control who can use the different endpoint actions this also includes custom viewset actions.

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

    class MyPermissions(permissions.DjangoModelPermissions):
    perms_map = {
    “GET”: [“%(app_label)s.view_%(model_name)s”],
    “OPTIONS”: [],
    “HEAD”: [],
    “POST”: [“%(app_label)s.add_%(model_name)s”],
    “PUT”: [“%(app_label)s.change_%(model_name)s”],
    “PATCH”: [“%(app_label)s.change_%(model_name)s”],
    “DELETE”: [“%(app_label)s.delete_%(model_name)s”],
    }

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

  2. I was more than happy to discover this website.

    I need to to thank you for your time for this particularly wonderful read!!
    I definitely really liked every part of it and i also have you saved as
    a favorite to see new information in your site.

    my web blog: JSON validator online

  3. great points altogether, you just gained a logo new reader.
    What would you suggest about your submit that you made a few days in the past?
    Any certain?

    • Thank you so much, and welcome aboard! I’m glad you enjoyed the points. As for recent posts, let me know if there’s anything specific you’re curious about, and I’d be happy to dive deeper or share extra insights!

  4. I think other website proprietors should take this web site as an model, very clean and great user friendly style and design, let alone the content. You are an expert in this topic!

Leave a Reply

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