In this tutorial, you will learn how to create a simple CRUD(Create Retrieve Update Delete) api by using the django rest framework.
Let's get started by creating a new virtual environment and Django project.
python -m venv env
env\Scripts\Activate #On Linux: source env/bin/activate
pip install django djangorestframework
django-admin startproject crudapi
cd crudapi
python manage.py startapp core
Now, add 'rest_frameowork' and our 'core.apps.CoreConfig' to INSTALLED_APPS in the settings file.
INSTALLED_APPS = [
...
'core.apps.CoreConfig',
'rest_framework',
]
Here, I am creating a model with only two fields 'name' and 'age'.
from django.db import models
class Employee(models.Model):
name = models.CharField(max_length=20)
age = models.IntegerField()
def __str__(self):
return self.name
Django rest serializers work exactly like Django forms. Here we are using ModelSerializer to verify the user input and save it to the database.
from rest_framework import serializers
from core.models import Employee
class EmployeeSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
fields = "__all__"
Here we are creating an employee list view that lets us add a new employee if the request type is POST and returns all the employees from the table if the request type is GET.
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from core.serializers import EmployeeSerializer
from core.serializers import Employee
@api_view(['GET', 'POST'])
def employees_list(request):
if request.method == 'GET':
employees = Employee.objects.all()
serializer = EmployeeSerializer(employees, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = EmployeeSerializer(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)
In the below detail view, We perform the following operations: Get a single employee, Update the data for a single employee, Delete an employee.
@api_view(['GET' ,'PUT', 'DELETE'])
def employee_detail(request, pk):
try:
employee = Employee.objects.get(pk=pk)
except Employee.DoesNotExist:
return Response(status=status.HTTP_400_BAD_REQUEST)
if request.method == 'GET':
serializer = EmployeeSerializer(employee)
return Response(serializer.data)
if request.method == 'PUT':
serializer = EmployeeSerializer(employee, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
if request.method == 'DELETE':
employee.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Now let's make the url routes for our views.
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('api/employees/', include('core.urls'))
]
Here we add two URLs: one is / that is mapped to employees_list function and the other is :pk/ mapped to employee_detail.
from django.urls import path
from core import views
urlpatterns = [
path('', views.employees_list),
path('<int:pk>/', views.employee_detail),
]
Finally, let's make the migrations and run the server.
python manage.py makemigrations
python manage.py migrate
python manage.py runserver
To test the API's I am going to use a simple tool called httpie that you can install in a single pip command.
pip install httpie
Create a new employee.
http POST http://127.0.0.1:8000/api/employees/ name=shivam age=22
HTTP/1.1 201 Created
...
{
"age": 22,
"id": 1,
"name": "shivam"
}
Get all the employees.
http http://127.0.0.1:8000/api/employees/
HTTP/1.1 200 OK
...
[
{
"age": 22,
"id": 1,
"name": "shivam"
}
]
Update a employee.
http PUT http://127.0.0.1:8000/api/employees/1/ name=shivamkumraa age=22
HTTP/1.1 200 OK
...
{
"age": 22,
"id": 1,
"name": "shivamkumraa"
}
Get a employee.
http http://127.0.0.1:8000/api/employees/1/
HTTP/1.1 200 OK
...
{
"age": 22,
"id": 1,
"name": "shivamkumraa"
}
Delete a employee
http DELETE http://127.0.0.1:8000/api/employees/1/
HTTP/1.1 204 No Content
...
Similar to Django's class-based view, the rest framework provides us APIView that you can use to inherit to make your own class-based API views.
from rest_framework.views import APIView
from django.http import Http404
class EmployeeList(APIView):
def get(self, request, format=None):
employees = Employee.objects.all()
serializer = EmployeeSerializer(employees, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = EmployeeSerializer(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)
Similarly we can make the Detail view as well.
class EmployeeDetail(APIView):
def get_object(self, pk):
try:
return Employee.objects.get(pk=pk)
except Employee.DoesNotExist:
raise Http404
def get(self, request, pk):
employee = self.get_object(pk)
serializer = EmployeeSerializer(employee)
return Response(serializer.data)
def put(self, request, pk):
employee = self.get_object(pk)
serializer = EmployeeSerializer(employee, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
employee = self.get_object(pk)
employee.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Now you need to update the URLs and call the as_view() function to the Views.
...
urlpatterns = [
path('', views.EmployeeList.as_view()),
path('<int:pk>/', views.EmployeeDetail.as_view()),
]
To further reduce the code lines you can use the power of generic views given by the rest framework. So the below code does the exact same things as above but in a lot fewer lines of code.
...
from rest_framework import generics
class EmployeeList(generics.ListCreateAPIView):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
class EmployeeDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer