Readers like you help support MUO. When you make a purchase using links on our site, we may earn an affiliate commission. Read More.

Django Rest Framework (DRF) is a Django framework that offers support for building REST APIs. Like Django, DRF allows you to build your API views with function-based or class-based views.

Although class-based views can be difficult to work with at first, they offer benefits like better code structure, reusability, inheritance, and conciseness.

Create a Recipe Manager API With Django REST Framework

A recipe manager app is a great way to learn about class-based views in DRF. Features such as adding, deleting, and editing recipes will help you understand how to implement CRUD (Create, Read, Update, Delete) operations. The following steps will teach you how to create a CRUD API.

You can find the code for this guide on GitHub.

Step 1: Install Django REST Framework and Configure Your Project

  1. Create a virtual environment for your project and install the following dependencies:
     pip install django djangorestframework
  2. Create a Django project called core with the following command:
     django-admin startproject core .
  3. Create an app called recipe_manager:
     python manage.py startapp recipe_manager
  4. Open your core/settings.py file and navigate to the INSTALLED_APPS list to register your apps:
     INSTALLED_APPS = [
        # custom apps
        'rest_framework',
        'recipe_manager',
    ]

Step 2: Create a Model for Your Recipe App

  1. Open your recipe_manager/models.py file and create a model for your app. Here's a basic example of a recipe model:
     # models.py
    from django.db import models

    class Recipe(models.Model):
        recipe_name = models.CharField(max_length=255)
        ingredients = models.TextField()
        instructions = models.TextField()
  2. Create migrations and migrate your model into the database with this command:
     python manage.py makemigrations && python manage.py migrate

Step 3: Create a Serializer for Your App

A serializer is a Django component that helps you convert complex data types, such as your query set, to a format that you can render, like JSON or XML, and vice versa.

To create a serializer, follow these steps:

  1. Create a file called recipe_manager/serializers.py.
  2. Import the serializers module as well as the model you want to serialize:
     # serializers.py
    from rest_framework import serializers

    from .models import Recipe # the model to serialize
  3. In the same file, create a serializer class for your model and define the Meta class in it:
     # serializers.py
    class RecipeSerializer(serializers.ModelSerializer):
        class Meta:
            model = Recipe
            fields = ('recipe_name', 'ingredients', 'instructions')
    In this code, the Meta class defines the model to serialize and the specific fields that the serializer should handle. The fields attribute can either be a list or a tuple. If you want to serialize all the fields in your model, you can do so like this:
     class Meta:
    fields = "__all__"

Step 4: Write a View for the CREATE Operation

You can create class-based views for your app by importing the generic view available in Django. You can read about these views from Django's official documentation. To implement the CREATE operation of CRUD, you should import the CreateAPIView. You should also import your serializer and model:

 # views.py
from rest_framework.generics import CreateAPIView

from .models import Recipe
from .serializers import RecipeSerializer

To implement the CREATE operation, you only need to specify the serializer your view should use. Here's an example:

 # Create view
class RecipeCreateView(CreateAPIView):
    serializer_class = RecipeSerializer

With this setup, you can make POST requests to your app.

Step 5: Write a View for the READ Operation

  1. To implement the READ operation, import the ListAPIView to your views. This view helps you list out model objects:
     # views.py
    from rest_framework.generics import CreateAPIView, ListAPIView
  2. Create a class for your views and specify the serializer and query set to use:
     # List view
    class RecipeListView(ListAPIView):
        serializer_class = RecipeSerializer
        queryset = Recipe.objects.all()
  3. Create a view to read a specific recipe. To do this, you need the RetrieveAPIView so add it to your list of imports:
     # views.py
    from rest_framework.generics import CreateAPIView, ListAPIView, RetrieveAPIView
    Next, create the view you need:
     # Retrieve view
    class RecipeRetrieveView(RetrieveAPIView):
        serializer_class = RecipeSerializer
        queryset = Recipe.objects.all()

Step 6: Write Views for the UPDATE and DELETE Operations

To implement the UPDATE and DELETE operations, you need the UpdateAPIView and DestroyAPIView respectively, so import them:

 from rest_framework.generics import (
    ListAPIView,
    CreateAPIView,
    RetrieveAPIView,
    UpdateAPIView, # new
    DestroyAPIView, # new
)

Next, create the views, just as you did before. This time, your views will inherit from the UpdateAPIView and DestroyAPIView, respectively:

 # Update view
class RecipeUpdateView(UpdateAPIView):
    serializer_class = RecipeSerializer
    queryset = Recipe.objects.all()

# Delete view
class RecipeDeleteView(DestroyAPIView):
    serializer_class = RecipeSerializer
    queryset = Recipe.objects.all()

Step 7: Create URLs for Your App

  1. Add this code to core/urls.py to configure your URLS:
     from django.urls import path, include

    urlpatterns = [
        path('api/', include('recipe_manager.urls'))
    ]
  2. Add the following code to your recipe_manager/urls.py file:
     from django.urls import path
    from . import views

    urlpatterns = [
        # List view (Read all)
        path('recipes/', views.RecipeListView.as_view(), name='recipe-list'),

        # Create view
        path('recipes/create/', views.RecipeCreateView.as_view(), name='recipe-create'),

        # Retrieve view (Read one)
        path('recipes/<int:pk>/', views.RecipeRetrieveView.as_view(), name='recipe-retrieve'),

        # Update view
        path('recipes/<int:pk>/update/', views.RecipeUpdateView.as_view(), name='recipe-update'),

        # Delete view
        path('recipes/<int:pk>/delete/', views.RecipeDeleteView.as_view(), name='recipe-destroy'),
    ]
    From the above code, you will observe that class-based views use the as_view() function to create their URL patterns. You can also read about the differences between a project and an app in Django if you're confused by their usage here.

Step 8: Test Your API Endpoints

From your project directory, run the following:

 python manage.py runserver 

This should start your server, perform some checks, and print a URL you can access it via.

You can now test your API endpoints by navigating to the respective URLs (e.g., /api/recipes/) and sending HTTP request methods for CRUD operations. You should see a default interface like this:

django's default api testing interface

Instead of using your browser, you can test your API with Postman.

Practicing DRY While Creating a CRUD API

DRY (Don't Repeat Yourself) is a programming principle you should adopt to improve the quality of your code.

Although the views written above work well, you can avoid a lot of repetition by using the ListCreateAPIView and the RetrieveUpdateDestroyAPIView generic views.

The ListCreateAPIView combines the ListAPIView and CreateAPIView, while the RetrieveUpdateDestroyAPIView combines the RetrieveAPIView, UpdateAPIView, and the DestroyAPIView.

You can modify your previous views to look like this:

 from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView

from .models import Recipe
from .serializers import RecipeSerializer

class RecipeListCreateAPIView(ListCreateAPIView):
    serializer_class = RecipeSerializer
    queryset = Recipe.objects.all()

class RecipeRetrieveUpdateDeleteAPIView(RetrieveUpdateDestroyAPIView):
    serializer_class = RecipeSerializer
    queryset = Recipe.objects.all()

This approach reduces the overall amount of code.

You can create URLs for the new views like this:

 from django.urls import path
from .views import RecipeListCreateAPIView, RecipeRetrieveUpdateDeleteAPIView

urlpatterns = [
    # List and Create view
    path('recipes/', RecipeListCreateAPIView.as_view(), name='recipe-list-create'),

    # Retrieve, Update, and Delete view
    path('recipes/<int:pk>/', RecipeRetrieveUpdateDeleteAPIView.as_view(), name='recipe-retrieve-update-destroy'),
]

You can test these endpoints with Postman or any API testing tool you prefer.

Generic Class-Based Views Make Your Work Easier

As seen above, generic class-based views can speed up the process of creating views. Now you only need to inherit the right APIView for your use case.

You should also ensure you adopt good programming practices, so you don't end up writing bad code.