SIMPLE  REST API WITH DJANGO REST FRAMEWORK.
Published: 2020-02-07 06:40:08



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.``


**GETTING STARTED**
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 


**RUN THE APPLICATION**

## 

     (simple_app) $ python manage.py runserver
   
``The application is running at http://127.0.0.1:8000/``

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fb77rllebnrpjzy7hy8a.png)


**Setup Django REST Framework**

## 

     (simple_app) $ pipenv install djangorestframework
   
Now lets add our Django app ``rest_api`` and ``restframework`` to INSTALLED_APPS list

``simple-app/settings.py``

## 
    # Application definition

     INSTALLED_APPS = [
     'django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
     '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.

**Model**
`` rest_api/models.py``

##
     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, 
              default=generate_id,
              editable=False
              )
         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 ``helpers.py`` in our app folder 

``rest_api/helpers.py``

##  
     import uuid
     from django.utils.http import int_to_base36

     LENGTH_OF_ID = 12

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

Now run this two commands on the ``terminal``
## 

     (simple_app) $ python manage.py makemigrations

## 

     (simple_app) $ python manage.py migrate

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

**Serialize The Customer Model**

Add a new file called ``serializer.py`` in our app folder

``rest_api/serializer.py``

##  

    from rest_framework import serializers
    from rest_api.models import Customer


    class CustomerSerializer(serializers.ModelSerializer):

         class Meta:
            model = Customer
            fields = ["id", "name", 
                       "product","is_active","created_at","updated_at"]

``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.`` 


**ViewSet**
``serializers translate information in both directions ``reads and writes``, views defines the functions ``read, create, update, delete`` which 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 ``views.py`` in our app folder.

``rest_api/views.py``

##  
    from rest_framework import viewsets
    from rest_framework.status import (
         HTTP_201_CREATED,
         HTTP_200_OK,
         HTTP_400_BAD_REQUEST,
         HTTP_404_NOT_FOUND,
         HTTP_204_NO_CONTENT
         )
    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(data=request.data)
            if not serializer.is_valid():
                return Response(status=HTTP_400_BAD_REQUEST)
            serializer.save()
            return Response(data=serializer.data, 
                             status=HTTP_201_CREATED)
``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 models.py 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 ``views.py`` in our app folder, inside ``CustomerViewSet`` just after the ``create method`` add the following lines
``rest_api/views.py``

## 
     def list(self, request):
        queryset = Customer.objects.filter(is_active=True)
        serializer = CustomerSerializer(queryset, many=True)
        return Response(data=serializer.data,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 ``views.py`` in our app folder, inside ``CustomerViewSet`` just after the ``list()`` method add the following lines
``rest_api/views.py``

## 
     def destroy(self, request, pk):
        try:
            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
        customer_detail.save()
        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 ``views.py`` in our app folder, inside ``CustomerViewSet`` just after the ``destroy()`` method add the following lines of code.
``rest_api/views.py``

## 
     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, data=request.data, partial=True)

        if serializer.is_valid():
            serializer.save()
            return Response(data=serializer.data, 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 ``serializer.py`` inside ``class CustomerSerializer`` just after ``class Meta`` add the following lines


``rest_api/serializer.py``

##
     def update(self, instance, validated_data):
        instance.name = validated_data.get("name",instance.name)
        instance.product = validated_data.get("product",instance.product)
        instance.save()
        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 url.py

``rest_api/url.py``
##  
    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/urls.py``, 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 ``rest_api.url.py`` file


``Start server``
## 
     (simple_app) $ python manage.py runserver

Fire up postman. url ``http://127.0.0.1:8000/api/v1/customer``

[url](http://127.0.0.1:8000/api/v1/customer)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/cmzofh8ly36stk3pgwec.png)


Feel free to run GET,DELETE and PATCH. 

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