Django Rest Framework Cheatsheet

🤔What is an API?

Deeksha Sharma
8 min readAug 24, 2023

API is a middleman that is used to connect two projects, services, and applications. There are three types of APIs:

✨Private

✨Partner

✨Public

With the help of an API, we are able to use a single backend for our web, desktop, or Android application. No need to write a backend again and again.

🤔What is a REST API?

If you are making your API using REST Architecture then it will be called as a REST API.

🤔What is MVT Model Architecture?

Models →Models are used to make schema that relates to the tables in the database.

✔After creating models we need to do migration →python manage.py makemigrations

✔To create table from this model →python manage.py migrate

😉We have to register our model in admin.py file also

🐱‍👓Installation of Django rest framework

pip install djangorestframework

#To check django version →django-admin — version

#To make a new project in djnago — ->django-admin startproject mysite

Add this in settings.py file

INSTALLED_APPS = [
...
'rest_framework',
]

🐱‍🏍Serialization-->Conversion of complex data type(Model Objects) into the Python Native Datatype(Dictionary) is known as serialisation.

🐱‍🏍Deserialization →Conversion of Python Native Datatype(Dictionary) into complex data type(Model Objects) is known as deserialization.

❤There are two types of serializers:-

🎁serializers.Serializer

🎁serializers.ModelSerializer

❤There are two types of views:-

🐱‍🐉Function based views

🐱‍🐉Class based views

How to create a Serializer?

from rest_framework import serializers

class MovieSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField()
description = serializers.CharField()
active = serializers.BooleanField()

🤞How to utilize a Serializer?

from imdb_clone.models import Movie
from imdb.api.serializers import MovieSerializer
from rest_framework.response import Response

def movie_list(request):
movies = Movie.objects.all()
serializer = MovieSerializer(movies, many=True)
return Response(serializer.data)

🎉GET REQUEST

from imdb_clone.models import Movie
from imdb.api.serializers import MovieSerializer
from rest_framework.response import Response
from rest_framework.decorators import api_view

@api_view()
def movie_list(request):
movies = Movie.objects.all()
serializer = MovieSerializer(movies, many=True)
return Response(serializer.data)

🎉POST REQUEST

def create(self, validated_data):
return Movie.objects.create(**validated_data)
@api_view(['GET', 'POST'])
def movie_list(request):
if request.method == 'GET':
movies = Movie.objects.all()
serializer = MovieSerializer(movies, many=True)
return Response(serializer.data)
if request.method == 'POST':
serializer = MovieSerializer(data = request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)

🎉UPDATE REQUEST

  #instance contains the old data before updation.validated_data contains the new data we want to put
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.description = validated_data.get('description', instance.description)
instance.active = validated_data.get('active', instance.active)
instance.save()
return instance
@api_view(['GET', 'PUT', 'DELETE'])
def movie_detail(request, pk):

#In PUT request we rewrite every field
if request.method == 'PUT':
movie = Movie.objects.get(pk=pk)
#Passing which object I want to update
serializer = MovieSerializer(movie, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)

🎉DELETE REQUEST

if request.method == 'DELETE':
movie = Movie.objects.get(pk=pk)
movie.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

Using Try and Except

if request.method == 'GET':
try:
movie = Movie.objects.get(pk=pk)
except Movie.DoesNotExist:
return Response({'Error':'Movie not found'}, status=status.HTTP_404_NOT_FOUND)

🎶Class Based Views

With class based views there will be no need to check the type of method using if condition. You will get it better after going through below examples:

👏Get and Post Method

class MovieListAV(APIView):

def get(self, request):
movies = Movie.objects.all()
serializer = MovieSerializer(movies, many=True)
return Response(serializer.data)

def post(self, request):
serializer = MovieSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)

👏Put and Delete Method

class MovieDetailAV(APIView):
def get(self, request, pk):
try:
movie = Movie.objects.get(pk=pk)
except Movie.DoesNotExist:
return Response({'Error':'Movie not found'}, status=status.HTTP_404_NOT_FOUND)
serializer = MovieSerializer(movie)
return Response(serializer.data)

def put(self, request, pk):
movie = Movie.objects.get(pk=pk)
#Passing which object I want to update
serializer = MovieSerializer(movie, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


def delete(self, request, pk):
movie = Movie.objects.get(pk=pk)
movie.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

👍While using class based views we have to use .as_view() in urls as shown below:

from imdb.api.views import MovieListAV, MovieDetailAV
##Using class based views
urlpatterns= [
path('list/', MovieListAV.as_view(), name='movie-list'),
path('<int:pk>', MovieDetailAV.as_view(), name='movie-detail')
]

😎Validations

We will be using validators in serializers and these validators will be used whenever we will be calling serializer.is_valid() in views.py

🌹Field-Level Validation: Used for checking a individual field.

    #validating name field.Value will be containing the field we are validating
def validate_name(self, value):
if len(value) < 2:
raise serializers.ValidationError("Name is too short!")
else:
return value

🌹Object-Level Validation:

    #validating whole object.Data will be containing the whole object    
def validate(self, data):
if data['name'] == data['description']:
raise serializers.ValidationError("Title and description should not be same")
else:
return data

🌹Validator:

def name_length(value):
if len(value) < 2:
raise serializers.ValidationError("Name is too short!")

class MovieSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
#name_length is a function that has to be passed to the validator
name = serializers.CharField(validators=[name_length])

👀 Model Serializer

This makes the code shorter than the normal serializer

#Model Serializer contains all the information about all fields
class MovieSerializer(serializers.ModelSerializer):

class Meta:
model = Movie
#all will include all the fields of the model
fields = "__all__"
#I can also add each field individually
fields = ['id', 'name', 'description']
#In exclude I can mention which fields I do not want
exclude = ['active']

#validators have to be added otherwise it will not know about them
#validating name field.Value will be containing the field we are validating
def validate_name(self, value):
if len(value) < 2:
raise serializers.ValidationError("Name is too short!")
else:
return value

#validating whole object.Data will be containing the whole object
def validate(self, data):
if data['name'] == data['description']:
raise serializers.ValidationError("Title and description should not be same")
else:
return data

😜Custom Serializer Fields

Using this we can add more fields in the model using serializers. This is useful when want to calculate something using existing fields of the model and then want to display that.

class MovieSerializer(serializers.ModelSerializer):

#created this new field in model using serializers
len_name = serializers.SerializerMethodField()

class Meta:
model = Movie
#all will include all the fields of the model
fields = "__all__"

#defining a function to get the length of the name field i.e get_fieldname
def get_len_name(self, object):
length = len(object.name)
return length

🐱‍💻Django Model Relationships

There are three types of relationships:

✔Many To Many

✔Many To One

✔One to One

class WatchList(models.Model):
title=models.CharField(max_length=50)
storyline=models.CharField(max_length=200)
#A platform can have multiple movies but a movie can be only on one platform
platform = models.ForeignKey(StreamPlatform, on_delete=models.CASCADE, related_name='watchlist')
#After this make migrations again because a platform field has been added

😎Nested Serializers

class StreamPlatformSerializer(serializers.ModelSerializer):
#One platform can have many movies
#Now each platform will show all the web shows it has due to this nested serializer
watchlist = WatchListSerializer(many=True, read_only=True)
class Meta:
model = StreamPlatform
fields = "__all__"

🤷‍♂️Serializer Relations

If we don’t want to get all the fields of related serializer like we are getting above, we can use serializer relations:

class StreamPlatformSerializer(serializers.ModelSerializer):
#It is going to return the field which is in str function in the model
watchlist = serializers.StringRelatedField(many=True)
class Meta:
model = StreamPlatform
fields = "__all__"

If we want to get links of the movies hosted on that particular platform, we can use this serializer relation:

class StreamPlatformSerializer(serializers.ModelSerializer):
#Here a link will be created to access movies on that particular platform
watchlist = serializers.HyperlinkedRelatedField(
many=True,
read_only=True,
#view_name should have the name mentioned in urls.py
view_name='watch-detail'
)
urlpatterns= [
path('list/', WatchListAV.as_view(), name='watch-list'),
path('<int:pk>', WatchDetailAV.as_view(), name='watch-detail'),
]
#This name watch-detail is important

HyperLinked Model Serializer

Similar to model serializer but the only difference is that it uses hyperlinks to represents relationships rather than primary key.

class StreamPlatformSerializer(serializers.HyperlinkedModelSerializer):
watchlist = WatchListSerializer(many=True, read_only=True)

class Meta:
model = StreamPlatform
fields = "__all__"
class StreamPlatformAV(APIView):

def get(self, request):
platform = StreamPlatform.objects.all()
serializer = StreamPlatformSerializer(platform, many=True, context={'request':request})
return Response(serializer.data)
urlpatterns= [
path('stream/<int:pk>', StreamPlatformDetailAV.as_view(), name='streamplatform-detail')
]
# name in this case has to be {modelname}-detail
class StreamPlatformDetailAV(APIView):

def get(self, request, pk):
try:
platform = StreamPlatform.objects.get(pk = pk)
except StreamPlatform.DoesNotExist:
return Response({'error':'Not Found'}, status=status.HTTP_404_NOT_FOUND)

serializer = StreamPlatformSerializer(platform, context={'request':request})
return Response(serializer.data)

🎂Serializer Relations

#one movie can have multiple reviews.Many To One relationship
class Review(models.Model):
rating = models.PositiveIntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
description = models.CharField(max_length=200, null=True)
watchlist = models.ForeignKey(WatchList, on_delete=models.CASCADE, related_name='reviews')
active = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
update = models.DateTimeField(auto_now=True)

def __str__(self):
return self.rating
class ReviewSerializer(serializers.ModelSerializer):
#Through this serializer we can do crud operations on review
class Meta:
model = Review
fields = "__all__"

#Model Serializer contains all the information about all fields
class WatchListSerializer(serializers.ModelSerializer):
#related name has to be written here which is reviews
#This serializer has access to reviews as read only
reviews = ReviewSerializer(many=True, read_only=True)

A QuerySet is a list of objects of a given model, QuerySet allow you to read data from database.

🐱‍🏍Generic APIView and Mixins

Although we have API Views even then we prefer mixins because for common tasks mixins are ready to use, we only have to provide some settings. Mixins are popular to perform common tasks superfast.

In simple words, mixins are a type of class based views that handles most common required action (create, update, retrieve, delete).

#View to access all the reviews on a particular watchlist
class ReviewList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
queryset = Review.objects.all()
serializer_class = ReviewSerializer

def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
urlpatterns= [
path('review/', ReviewList.as_view(), name='review-list'),
]
#View to access a particular review
class ReviewDetail(mixins.RetrieveModelMixin, generics.GenericAPIView):
#Review Object
queryset = Review.objects.all()
#Review Serializer
serializer_class = ReviewSerializer

#Retrieve Action:This will give the review of a particular movie.
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
urlpatterns= [
path('review/<int:pk>', ReviewDetail.as_view(), name='review-detail')
]

In the above code example we used mixins along with generic view. Now let’s use generic view directly to make this more shorter to code. Generic View and Mixins are the way to make code more shorter.

😊Concrete Class Based Views

When we were using mixins in the above code then we were writing function to do CRUD operations. But with concrete class based views we even don’t need to write those as they already have those.So with concrete class based views we can eliminate even this need of writing functions. Super short our views will become!

#See the magic of concrete class based views.We need only to write two lines and we are done
class ReviewList(generics.ListCreateAPIView):
queryset = Review.objects.all()
serializer_class = ReviewSerializer

#RetrieveUpdateDestroyAPIView is going to give me options for get,put and delete
class ReviewDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Review.objects.all()
serializer_class = ReviewSerializer
urlpatterns= [
# path('review/', ReviewList.as_view(), name='review-list'),
# path('review/<int:pk>', ReviewDetail.as_view(), name='review-detail')
#Instead of getting all the reviews collectively.Let's have review for a particular web show
path('stream/<int:pk>/review', ReviewList.as_view(), name='review-list'),
path('stream/review/<int:pk>', ReviewDetail.as_view(), name='review-detail'),
]

🙂Overwriting Queryset

In the above code example :

(path(‘stream/<int:pk>/review’, ReviewList.as_view(), name=’review-list’),

We are trying to get all the reviews for a particular web show. Now this view is going to fetch all the reviews of all the webshows. So we need to overwrite the queryset here:

#This needs to be changed by overwriting queryset
class ReviewList(generics.ListCreateAPIView):
queryset = Review.objects.all()
serializer_class = ReviewSerializer

#This has to be used
class ReviewList(generics.ListAPIView):
# queryset = Review.objects.all()
serializer_class = ReviewSerializer

def get_queryset(self):
pk = self.kwargs['pk']
return Review.objects.filter(watchlist=pk)

We can also write a view so that we can create reviews on a particular watchlist.

urlpatterns= [
path('stream/<int:pk>/review-create', ReviewCreate.as_view(), name='review-create'),
]
class ReviewCreate(generics.CreateAPIView):
serializer_class = ReviewSerializer

#overwriting the default provided perform_create function according to our requirement
def perform_create(self, serializer):
pk = self.kwargs.get('pk')
watchlist = WatchList.objects.get(pk=pk)
serializer.save(watchlist=watchlist)

🧠Viewsets and Routers

When we have requirements like that we want to show all the list and we also want to show the detail of each element of the list, then we use Viewsets.

Like we are going to combine these two:

path('stream/', StreamPlatformAV.as_view(), name='stream'),
path('stream/<int:pk>', StreamPlatformDetailAV.as_view(), name='streamplatform-detail'),

Since we will be combining these two so there is no need of two separate URL for this, and for this purpose we use Routers.

#ViewSets
class StreamPlatform(viewsets.ViewSet):
def list(self, request):
queryset = StreamPlatform.objects.all()
serializer = StreamPlatformSerializer(queryset, many=True)
return Response(serializer.data)

def retrieve(self, request, pk=None):
queryset = StreamPlatform.objects.all()
watchlist = get_object_or_404(queryset, pk=pk)
serializer = StreamPlatformSerializer(StreamPlatform)
return Response(serializer.data)

Routers helps us to combine all types of links.

So, this is all about Django Rest Framework. Happy Coding!!

--

--