DEV Community

Habib Audu
Habib Audu

Posted on



Hi guys! In this post am going to be taking us on how to build a simple rest Api with Django rest framework, performing all CRUD (create, read, update, delete) operations. Django rest framework is a robust toolkit for building web APIs.

A RESTful API is an application programming interface (API) that uses HTTP requests to create, Read, update and delete data.It defines a set of functions which developers can perform requests and receive responses via HTTP protocol.

First thing first lets setup a new Django project.

install pipenv if you don't already have it

 $ pip3 install pipenv

Create a virtual environment or activate one

  $ pipenv shell

Install Django

 $ pipenv install django 

Create a Django project

 $ django-admin startproject simple_app

Create a Django app

  $ django-admin startapp rest_api 


 (simple_app) $ python runserver

The application is running at

Alt Text

Setup Django REST Framework

 (simple_app) $ pipenv install djangorestframework

Now lets add our Django app rest_api and restframework to INSTALLED_APPS list


# Application definition

 'rest_api.apps.RestApiConfig', # Add this line
 'rest_framework',              # Add this  line

We will be creating a sample customer API, where we can perform the various CRUD operations. Open our models file and add the following lines.


 from django.db import models
 from rest_api.helpers import LENGTH_OF_ID, generate_id

 class Customer(models.Model):

     id = models.CharField(
          max_length=LENGTH_OF_ID, primary_key=True, 
     name = models.CharField(max_length=100, null=False)
     product = models.CharField(max_length=100, null=False)
     is_active = models.BooleanField(default=True)
     updated_at = models.DateTimeField(auto_now=True)
     created_at = models.DateTimeField(auto_now_add=True)

Add a new file called in our app folder


 import uuid
 from django.utils.http import int_to_base36


 def generate_id() -> str:
     """Generates random string with length of `ID_LENGTH`"""
     return int_to_base36(uuid.uuid4().int)[:LENGTH_OF_ID] helps us generate unique ids of length 12

Now run this two commands on the terminal

 (simple_app) $ python makemigrations
 (simple_app) $ python migrate

Makemigrations is responsible for packaging up your model changes into individual migration files, so anytime a change is made on your you need to run python makemigrations. Migrate is responsible for applying those changes to your database, you should also run python migrate every time you generate a new migration file.

Serialize The Customer Model

Add a new file called in our app folder


from rest_framework import serializers
from rest_api.models import Customer

class CustomerSerializer(serializers.ModelSerializer):

     class Meta:
        model = Customer
        fields = ["id", "name", 

A serializer converts data stored in the database and data you define in the models into a format which is more easily communicated via an API.
Now django models represent data stored in your database, but an API will need to transmit information in a less complex structure.
so the serializer convert your data into JSON so it can be transmitted over an API.

serializers translate information in both directionsreads and writes, views defines the functionsread, create, update, deletewhich will be available via the API. It has the following built-in operations.

create(): To Create an instance.
retrieve(): Retrieve/Read an instance.
update() : Update all fields of an instance.
partial_update(): Update selected fields of an instance.
destroy(): Deletes an instance.
list(): Retrieve/Read an instance.

To CREATE a customer lets populate our view open in our app folder.


from rest_framework import viewsets
from rest_framework.status import (
from rest_framework.response import Response
from rest_api.models import Customer
from rest_api.serializer import CustomerSerializer

class CustomerViewSet(viewsets.ViewSet):
    '''ViewSet to add a customer'''
    serializer_class = CustomerSerializer

    def create(self, request):
        serializer = self.serializer_class(
        if not serializer.is_valid():
            return Response(status=HTTP_400_BAD_REQUEST)
        return Response(, 

we have imported some modules here, `viewsets` from rest_framework, which is going to serve as the `superclass` to our `CusterViewSets`,various status code from rest_framework to send appropriate status_code with our response,`Response` which actually sends the response, Customer model from and our CustomerSerializer. Inside create(), we send the request data into the serializer for serialization, we call `is_valid()` to check the validity of our data, save() if its valid and return the serialized data with a status code of 200.

To GET all customers lets populate our view open in our app folder, inside CustomerViewSet just after the create method add the following lines

 def list(self, request):
    queryset = Customer.objects.filter(is_active=True)
    serializer = CustomerSerializer(queryset, many=True)
    return Response(,status=HTTP_200_OK)

Here we query our Customer table/model for all instances of customer where the value of `is_active` is `True` and pass the resulting `queryset` into our CustomerSerializer for serialization, then return the serialized data and a status code of 200.

To DELETE a customer lets populate our view open in our app folder, inside CustomerViewSet just after the list() method add the following lines

 def destroy(self, request, pk):
        customer_detail = Customer.objects.get(pk=pk, is_active=True)
    except Customer.DoesNotExist:
        return Response(status=HTTP_404_NOT_FOUND)
    customer_detail.is_active = False
    return Response(status=HTTP_204_NO_CONTENT)

Here we try to get an instance of customer which has `pk` that was passed and has is_active as True, if thats is successful we change `is_active` to `False`, save() and return status of 204.

To PATCH a customer lets populate our view open in our app folder, inside CustomerViewSet just after the destroy() method add the following lines of code.

 def partial_update(self, request, pk):
    customer = Customer.objects.filter(pk=pk).first()
    if not customer:
        return Response(status=HTTP_404_NOT_FOUND)
    serializer = self.serializer_class(
        customer,, partial=True)

    if serializer.is_valid():
        return Response(, status=HTTP_200_OK)

Here we try to get an instance of customer with the `primary_key` that was passed `(pk)`, if thats is successful we pass the customer instance and request data into our serializer, if our serializer is valid we save() and return a response with serialized data and appropriate status code.

Next open inside class CustomerSerializer just after class Meta add the following lines


 def update(self, instance, validated_data): = validated_data.get("name",
    instance.product = validated_data.get("product",instance.product)
    return instance

To update we call the update() method inside the serializer class, it takes an instance of the object to update and validated data from the serializer. It update the instance save and return the instance to the view.

Now lets map to url, create another file in our django app


from django.urls import path, include
from django.conf.urls import url
from rest_api.views import (CustomerViewSet)
from rest_framework.routers import DefaultRouter

router = DefaultRouter(trailing_slash=False)
router.register(r'customer', CustomerViewSet, basename='customer')

urlpatterns = [
    url(r'', include(router.urls)),

Now register this url in our project urls, Open simple_api/, you can if you like remove all uncommented code there and add the following lines

 from django.urls import path, include

 urlpatterns = [
     path("api/v1/", include("rest_api.url")),

This just includes the url we created in our file

Start server

 (simple_app) $ python runserver

Fire up postman. url


Alt Text

Feel free to run GET,DELETE and PATCH.

I don't think you can go wrong with django restframework wink.
Thanks for Reading cheers...

Top comments (0)

🌚 Friends don't let friends browse without dark mode.

Sorry, it's true.