The Ultimate Tutorial for Django REST Framework: CRUD (Part 1)

Dominik Kozaczko - Backend Engineer

Dominik Kozaczko

24 July 2018, 7 min read

thumbnail post

What's inside

  1. Part 1 - CRUD

Joining a project that uses Django REST Framework (DRF) is often stressful because of things like spaghetti code or antipatterns. I encounter the problem so often that at some point I began to wonder where that issue actually comes from. The DRF documentation is comprehensive and generally well-organized, so it should help in eliminating this issue.

That’s when I decided to analyze the thought process of a developer who has a project that is more or less ready and now wants to add a REST API to it.

Usually, he or she will have a look at this tutorial. Once the developer reads it and gets the basics, it’s time to implement its content in the application. In most cases, we want to have CRUD, and then some additional options for filtering available objects, and then some other functional endpoints like statistics or a search engine.

And suddenly, it dawned on me:

The DRF tutorial is written in reverse order, and that’s where the problem comes from. Have a look at it, and you’ll see that it attempts to show low-level versatility instead of meeting the expectations of developers and showing high-level acronyms instead.

When we read the tutorial, we first learn about the details of views, serializers, and, only at the very end, about ViewSets - a wonderfully compact way for binding everything into a neat, clear and manageable whole. But most developers never get to this place because they have already built a relatively functional API and decided to abandon the tutorial in favor of the API Guide, searching for ways of implementing their project requirements.

This article is the first one in a series where I plan to show you Django REST Framework from the general to detailed overview and help you avoid reinventing the wheel.

Follow this series, and you’ll get clear and highly manageable code that won’t bring you shame when you show or transfer it to others.

BTW. You can get the entire content of this series in a free PDF. Sounds great? Follow this link to download your copy!

guide to the django rest framework

Part 1 - CRUD

If you’re reading this, you probably already have at least the application’s wireframe, and you want to add a REST API to enable basic operations on objects such as Create, Retrieve, Update and Delete (CRUD). That’s what we will do in this article.

For starters, let's prepare an example application for managing things we lend to our friends. Then, we need to install Django REST Framework.

; cd your project’s directory and activate its virtualenv
$ ./ startapp rental
$ pip install djangorestframework

Next, we need to modify the INSTALLED_APPS parameter in file.

    # previous apps



from django.db import models

class Friend(models.Model):
    name = models.CharField(max_length=100)

class Belonging(models.Model):
    name = models.CharField(max_length=100)

class Borrowed(models.Model):
    what = models.ForeignKey(Belonging, on_delete=models.CASCADE)
    to_who = models.ForeignKey(Friend, on_delete=models.CASCADE)
    when = models.DateTimeField(auto_now_add=True)
    returned = models.DateTimeField(null=True, blank=True)

To make our objects available through the API, we need to perform a serialization - reflect the data contained in the object textually. The default format here is JSON, although DRF allows serialization to XML or YAML. The reverse process is called deserialization.

Both processes are defined in objects referred to as serializers. DRF offers developers with a convenient class to create serializers for Django models easily, so we have to provide only some basic information such as the model that will be served in the serializer and the fields to which we want to give access.


from rest_framework import serializers
from . import models

class FriendSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Friend
        fields = ('id', 'name')

class BelongingSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Belonging
        fields = ('id', 'name')

class BorrowedSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Borrowed
        fields = ('id', 'what', 'to_who', 'when', 'returned')

Now we need to create views that will handle each of the operations we want to perform on our objects.

Consider for a moment their names and how to combine them with methods available in the HTTP standard.

Create - that one is rather straightforward. Standard support for it comes from the HTTP POST method. Because we’re creating a set element here (in particular, the object’s ID will be only determined now), we treat this method as an operation on the list: creating an element.

Retrieve - we have two options here: we can download a list of objects of a given type (list) or one specific object (retrieve). In both cases, GET will be the adequate HTTP method.

Update - There are two HTTP methods available here: PUT and PATCH. The difference between them is that according to its definition, PUT requires all attributes of the object - including those that have not changed. PATCH, on the other hand, allows entering only those fields that have actually changed, which is why it’s more popular. Using the PUT or PATCH method to update multiple objects is rare and DRF only supports updating single object in its default CRUD.

Delete - this deletes one or many objects. The HTTP method here will be DELETE. In practice, for security reasons, it’s usually not possible to remove several objects at the same time and again, DRF only supports this operation on single objects in its default CRUD. .

Let's summarize all of the above:

OperationHTTP methodendpoint type
Retrieve manyGETlist
Retrieve oneGETdetail
UpdatePUT / PATCHdetail

To support such a set of operations, DRF provides a handy tool - ViewSet. It takes the idea behind the standard class-based views from Django to a higher level. What it does is packing the above set into one class with the automatic creation of appropriate URL paths.

So let’s see how that looks like in practice:

To start, let's create a ViewSet that will support our models. DRF provides the ModelViewSet thanks to which the required amount of code is reduced to the minimum:


from rest_framework import viewsets
from . import models
from . import serializers

class FriendViewset(viewsets.ModelViewSet):
    queryset = models.Friend.objects.all()
    serializer_class = serializers.FriendSerializer

class BelongingViewset(viewsets.ModelViewSet):
    queryset = models.Belonging.objects.all()
    serializer_class = serializers.BelongingSerializer

class BorrowedViewset(viewsets.ModelViewSet):
    queryset = models.Borrowed.objects.all()
    serializer_class = serializers.BorrowedSerializer

Then there’s the last part - connecting all this to the URL tree of our project. And here we get a very convenient tool as well - routers. DRF provides two of the most important classes that differ only in that one of them shows the API structure when downloading / (root), and the other doesn’t.

Our viewsets will be hooked up as follows: (global, next to

from rest_framework import routers
from core import views as myapp_views

router = routers.DefaultRouter()
router.register(r'friends', myapp_views.FriendViewset)
router.register(r'belongings', myapp_views.BelongingViewset)
router.register(r'borrowings', myapp_views.BorrowedViewset) (global)

from django.urls import include, path
from django.contrib import admin
from .api import router

urlpatterns = [
    path('api/v1/', include(router.urls)),

Let's test the API we created.

$ ./ makemigrations $ ./ migrate $ ./ runserver

Open this address in your browser (in standard configuration):

DRF automatically creates views that allow performing API queries from the browser level:

Django Rest Framework - Example of Use

View that allow performing API queries from the browser level - DRF Example

Experiment and check the effects in the django-admin panel.


At this point, we receive a ready API that supports CRUD for our models. Please note that we don’t have any security against unauthorized access here yet.

We will deal with the user login and registration process in the next article in this series.

Be sure to catch up with the work we’ve completed in other parts of the series:

Dominik Kozaczko - Backend Engineer

Dominik Kozaczko

Backend Engineer

Dominik has been fascinated with computers throughout his entire life. His two passions are coding and teaching - he is a programmer AND a teacher. He specializes mostly in backend development and training junior devs. He chose to work with Sunscrapers because the company profoundly supports the open-source community. In his free time, Dominik is an avid gamer.


django rest framework, python


Recent posts

See all blog posts

Are you ready for your next project?

Whether you need a full product, consulting, tech investment or an extended team, our experts will help you find the best solutions.

Hi there, we use cookies to provide you with an amazing experience on our site. If you continue without changing the settings, we’ll assume that you’re happy to receive all cookies on Sunscrapers website. You can change your cookie settings at any time.