Search icon
Cart icon
Close icon
You have no products in your basket yet
Save more on your purchases!
Savings automatically calculated. No voucher code required
Arrow left icon
All Products
Best Sellers
New Releases
Learning Hub
Free Learning
Arrow right icon
Django in Production
Django in Production

Django in Production: Expert tips, strategies, and essential frameworks for writing scalable and maintainable code in Django

By Arghya Saha
$29.99 $9.99
Book Apr 2024 348 pages 1st Edition
$29.99 $9.99
$15.99 Monthly
$29.99 $9.99
$15.99 Monthly

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon AI Assistant (beta) to help accelerate your learning
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Buy Now

Product Details

Publication date : Apr 5, 2024
Length 348 pages
Edition : 1st Edition
Language : English
ISBN-13 : 9781804610480
Category :
Table of content icon View table of contents Preview book icon Preview Book

Django in Production

Setting Up Django with DRF

In 2003, the Django project was started by developers Adrian Holovaty and Simon Willison from World Online, a newspaper web operation company, and was open sourced and first released in the summer of 2005. When Django was first built, the world was still using dial-up modem internet connections, mobile devices were still not popular, smartphones didn’t see the daylight, and people would access web pages through their desktops and laptops. Django was the perfect framework that had all the features needed to build a web application for that age.

Over the last two decades, technology has evolved drastically:

  • We have moved from dial-up internet connections to 4G/5G internet connections
  • 55% of the world’s internet traffic came from mobile devices in 2022 (

In this book, we shall see how to build a modern web application using Django and deep dive into the core concepts that a developer should know to create a scalable web application for startups. A developer building a product for a startup is expected to be more than just a regular developer writing code in Django; they are expected to develop their code, write tests for the business logic, deploy their applications to the web, and finally keep monitoring the service they have deployed. Here, we will learn how easy it is to develop web applications with Django and the best practices that developers in the industry follow, especially in startups, to make their development cycle easier and faster.

In this first chapter, we shall learn the basics of Django and how to set up a Django project and structure the project folders. Since we shall mostly work with RESTful APIs throughout this book, we will learn about the conventions of the REST API and the crux of setting up a Django project with Django Rest Framework (DRF) for creating RESTful APIs. We shall also focus on versioning APIs and how we can implement versioning using DRF. DRF gives us the flexibility to create both functional and class-based views; we shall learn about them in this chapter, along with their pros and cons.

We will cover the following topics:

  • Why Django?
  • Creating a “Hello World” web app using Django and DRF
  • Creating RESTful endpoints with DRF
  • Working with views using DRF
  • Introducing API development tools

Technical requirements

In this chapter, we shall do the basic project setup and also write our first Hello World app. Though this book is for developers who already know how to write a basic web application, anyone with decent programming skills can pick up this book and learn how to create a scalable Django web application. The following are the skill sets that you should possess to follow this chapter:

  • Good Python programming knowledge and familiarity with packages and writing loops, conditional statements, functions, and classes in Python.
  • A basic understanding of how web applications work and have written some form of API or web app before.
  • Even though we shall try to cover most of the concepts from scratch, having basic knowledge of Django would be great. The Django Girls tutorial is a good resource to learn the basics:

You can find the code for this chapter in this book’s GitHub repository:

Important note

If you have any doubts about any of the topics mentioned in this or other chapters, feel free to create GitHub issues that specify all the relevant information ( or join our Django in Production Discord channel and ask us. Here is the invite link for the Discord server, where you can reach me directly:

Why Django?

Django is a web framework based around Python, one of the most popular and easy-to-learn coding languages out there. Since Python is the go-to language for data science and artificial intelligence/machine learning, developers can easily learn Django without having to learn an additional language for building web applications.

Django’s tagline, “Django – The web framework for perfectionists with deadlines,” proves its commitment to faster and more efficient development, further emphasized by its batteries-included principle that all the basic and widely used functionalities come out of the box with the framework rather than us having to install additional packages. This gives Django an additional advantage over other frameworks, such as Flask.

What is available with Django?

Django has evolved in the last decade and is currently in version 5.x, which has some promising new features, such as asynchronous support. However, the core modules of Django are still the same, with the same principles. When a new developer wants to learn Django, an organization wants to pick Django for their new project, or a startup with limited resources is looking to pick the perfect framework for their tech stack, they want to know why they should learn about Django. To answer this question, we shall learn more about the features of Django.

Let’s look at the salient features of Django that the framework provides out of the box:

  • In any organization, speed of execution is very important for the success of a product. Django was designed to help developers take applications from the concept phase to the product phase at blazing speed.
  • Django takes care of user authentication, content administration, site maps, RSS feeds, and many more fundamental web tasks that developers look for in any framework.
  • Security is a serious concern for any organization and Django helps developers avoid common security pitfalls.
  • Websites such as Mozilla, Instagram, Disqus, and Pinterest all are built using Django, which makes Django a battle-tested framework that scales.
  • Django’s versatile framework can be used for different purposes, from content management systems to social networks to scientific computing platforms.

But the question of Django still being relevant is very subjective. Ultimately, it depends upon the use case. We know Django is a good web development framework, however, because more than 55% of the world’s internet traffic comes from mobile devices using Android or IOS apps, you may be wondering whether Django is relevant for building features for mobile users? Django as a standalone framework might not be sufficient for today’s modern web development where more and more organizations are moving towards API first development, but when integrated with frameworks like Django Rest Framework (DRF), Tastypie, etc, Django becomes the go-to framework for developers.

For start-ups with limited time and resources, it becomes even more crucial to choose a framework where they don’t have to build every feature from the ground up, but rather leverage the framework to do most of the heavy lifting.

Let’s quickly look a little more at the framework principle that Django uses: the MVT framework.

What is the MVT framework?

Most of us have heard of MVC frameworks (Model-View-Controller), which represent a paradigm of modern web frameworks where we have the following:

  • Model represents the data and business logic layer
  • View represents how the data is presented to the user in the UI/design layout
  • Controller updates the model and/or view based on the user’s input

Django considers the standard names debatable, hence why it has its own interpretation of MVC. Here, we have the following:

  • View represents which data is shown to the end user and not how the data is represented
  • Template represents how the data is represented to the end user
  • Model represents the data layer

That’s why Django follows the MVT framework (Model-View-Template). But now, the question is, what is the controller in Django? The framework itself is the controller since it handles the whole routing logic using its built-in features.

Important note

You don’t need to deep dive into MVT concepts since this concept becomes muscle memory as you write more code in Django.

MVT is a concept where we use templates, but in today’s world, most of the products are built for multiple domains such as mobile, IoT, and SaaS platforms. To build products for all these domains, the developer ecosystem has also evolved; now, organizations are moving toward an API-first development approach ( This means that APIs are “first-class citizens”; every feature in the product is built with an API-first model, which helps in creating a better client (mobile apps, frontend applications, and so on) and server integration. It involves establishing a contract between the client and the server so that each team can work in parallel without much dependency. Once both teams finish their work, the integration and development cycle of a product becomes much faster with a better developer experience.

The growing use case of mobile device means it is important to build platform-agnostic backend APIs that can be consumed by any client, Android app, iOS app, browser frameworks, and so on. Is Django, as an individual MVT framework, able to serve all these needs? No. The amount of additional effort required to use the out-of-the-box features of Django for creating APIs is similar to reinventing the wheel. That’s why most organizations use Django’s REST framework, along with Django, to create APIs.

Important note

In this book, instead of focusing on templates and standalone web applications built with Django, we shall focus on creating APIs using Django with DRF. For information on getting started with just Django, one of my favorite resources is the Django Girls tutorial:

Now that we have seen what the MVT framework is and how Django is an MVT framework, let’s create a basic Hello World web application using Django and set up our project structure and development environment.

Creating a “Hello World” web app with Django and DRF

As mentioned previously, Django is a Python-based web framework, so we need to write the code using the Python programming language. If you are already using Linux or macOS-based systems, then Python comes preinstalled. However, for Windows systems, you have to install it by following the instructions on the official Python website:

We shall also use virtualenv as our preferred tool to manage different environments for multiple projects, allowing us to create isolated Python environments.

Important note

We are not going to deep dive into virtualenv since we expect you to know how and why we use virtualenv for different projects. You can find details about virtualenv on its official website:

First, let’s create a virtual environment with the latest Python version (preferably >3.12.0). The following commands will work for Linux/Unix/macOS; for Windows, please check the next section:

pip install virtualenv
virtualenv -p python3 v_env
source /path to v_env/v_env/bin/activate

Now, we will break down what the code means:

  • pip install virtualenv installs virtualenv on the system. pip is the built-in package manager that comes with Python and is already preinstalled on Mac and most Linux environments.
  • virtualenv -p python3 v_env creates a new virtual environment with the name v_env (this is just the name we have given to our virtual environment; you can give another relevant name). The -p python3 flag is used to tell us which interpreter should be used to create the virtual environment.
  • source /path to v_env/v_env/bin/activate executes the activate script, which loads the virtual Python interpreter as our default Python interpreter in the shell.

Now that the Python virtual environment has been set up, we shall focus on managing the package dependency. To install the latest release of Django, run the following command:

pip install Django==5.0.2

For Windows systems, download Python 3.12 or higher from and install it by following the wizard. Remember to click the Add python.exe to PATH checkbox in the installation step.

To verify your Python installation, use the following command in the terminal:

C:\Users\argo\> python --version
Python 3.12.0

Once Python has been installed successfully, you can use the following command to set up a virtual environment and install Django:

py -m pip install --user virtualenv
py -m venv venv
.\<path to venv created>\venv\Scripts\activate
// to install Django
pip install Django==5.0.2

The explanation for the Windows-specific commands is the same as what we explained for Linux/MacOS systems.

Important note

We are not using poetry, PDM, pipenv, or any other dependency and package management tools to avoid overcomplicating the initial setup.

Furthermore, we prefer to use a Docker environment to create more isolation and provide a better developer experience. We shall learn more about Docker in Chapter 10.

With the previous command, our local Python and Django development environments are ready. Now, it’s time to create our basic Django project.

Creating our Django hello_world project

We all love the django-admin command and all the boilerplate code it gives us when we create a new project or application. However, when working on a larger project, the default project structure is not so helpful. This is because when we work with Django in production, we have many other moving parts that need to be incorporated into the project. Project structure and other utilities that are used with a project are always opinionated; what might work for you in your current project might not work in the next project you create a year down the line.

Important note

There are plenty of resources available on the internet that will suggest different project structures. One of my favorites is django-cookiecutter. It gives you a lot of tools integrated into the project and gives you a structure that you can follow, but it can be daunting for any new beginner to start since it integrates a lot of third-party tools that you might not use, along with a few configurations that you might not understand. But instead of worrying about that, you can just follow along with this book!

We shall create our own minimalistic project structure and have other tools integrated with our project in incremental steps. First, let’s create our hello_world project with Django:

mkdir hello_world && cd hello_world
mkdir backend && cd backend
django-admin startproject config .

Here, we have created our project folder, hello_world, and then created a subfolder called backend inside of it. We are using the backend folder to keep all the Django-related code; we shall create more folders at the same level as the backend subfolder as we learn more about the CI/CD features and incorporate more tools into the project. Finally, we used the Django management command to create our project.

Important note

Note the . (dot), which we have appended to the startproject command; this tells the Django management command to create the project in the current folder rather than create a separate folder config with the project. By default, if you don’t add ., then Django will create an additional folder called config in which the following project structure will be created. For better understanding, you can test the command with and without . to get a clear idea of how it impacts the structure.

After executing these commands, we should be able to see the project structure shown here:

Figure 1.1: Expected project structure after executing the commands

Figure 1.1: Expected project structure after executing the commands

Now that our project structure is ready, let’s run python runserver to verify our Django project. We should see the following output in our shell:

Figure 1.2: The python runserver command’s output in the shell

Figure 1.2: The python runserver command’s output in the shell

Please ignore the unapplied migrations warning stating You have 18 unapplied migrations(s) displayed in red in the console; we shall discuss this in detail in the next chapter when we learn more about the database, models, and migrations.

Now, go to your browser and open http://localhost:8000 or (if the former fails to load). We shall see the following screen as shown in Figure 1.3, which verifies

that our server is running successfully:

Please note

You can use http://localhost:8000 or to open the Django project in your browser. If you face any error for http://localhost:8000, then please try using for all the URLs mentioned in this book.

Figure 1.3: Our Django server running successfully with port 8000

Figure 1.3: Our Django server running successfully with port 8000

Now, let’s create our first hello_world view. To do this, follow these steps:

  1. Open the config/ file.
  2. Add a new view function in hello_world.
  3. Link the hello_world view function to the hello-world path.

    Our config/ file should look like the following code snippet:

    from django.contrib import admin
    from django.http import HttpResponse
    from django.urls import path
    def hello_world(request):
        return HttpResponse('hello world')
    urlpatterns = [
        path('hello-world/', hello_world)
  4. Open to get the result shown in Figure 1.4:
Figure 1.4: browser response

Figure 1.4: browser response

So far, we have seen how to create the project folder structure and create our first view in Django. The example we have used is one of the smallest Django project examples that doesn’t involve an app. So, let’s see how we can create apps in Django that can help us manage our project better.

Creating our first app in Django

A Django app can be considered a small package performing one individual functionality in a large project. Django provides management commands to create a new app in a project; these are built-in commands that are used to perform repetitive and complex tasks. The Django community loves management commands since they take away a lot of manual effort and encapsulate a lot of complicated tasks, such as migrations and more. We shall learn more about Django management commands in the following chapters, where we will create a custom management command. However, whenever you see a command followed by, that is a Django management command.

So, let’s create a new demo_app using the Django management command interface:

python startapp demo_app

Running this command will create the folder structure shown here:

Figure 1.5: Project structure with demo_app added

Figure 1.5: Project structure with demo_app added

We can see that a demo_app folder has been created that contains the boilerplate code generated by Django for a new app.

Important note

One important step we must do whenever we create a new app is to tell Django about the new app. Unfortunately, this doesn’t happen automatically when you create a new app using the Django management command. It is a manual process where you need to add the details of the new app in the INSTALLED_APPS list in the file. Django uses this to identify all the dependency apps added to the project and check for any database-related changes or even register for signals and receivers.

Though adding a new app to the INSTALLED_APPS list is not required for us currently, since we are not using models for Django to automatically identify any database-related changes, it is still good practice to do so. Our INSTALLED_APPS list should look like this:


Remember to put a comma (,) after every entry of a new app; this is one of the most common mistakes developers make and it causes Django to append two app names into one and generate a syntax error before finally correcting it.

Important note

In Django, third-party app integrations are also done via INSTALLED_APPS, so we shall see best practices around how to maintain INSTALLED_APPS in the following sections.

Now that we have created a new Django app with the boilerplate code, we can link the app view with

Linking app views using

In this section, we shall link, which was created by the Django management command. is used to add business logic to the application endpoints. Just like we added the hello_world functional view in the previous section, we can add different functional or class-based views in the file.

Let’s create a simple hello_world functional view in our demo_app/ file:

from django.http import HttpResponse
def hello_world(request, *args, **kwargs):
    return HttpResponse('hello world')

As our project grows and the number of apps increases, our main file will become more and more cluttered, with hundreds of URL patterns in a single file. So, it is favorable to break down the main config/ file into smaller files for each app, which improves the maintainability of the project.

Now, we will create a new file called backend/demo_app/ where we shall add all the routes related to demo_app. Subsequently, when we add more apps to the project, we shall create individual files for each app.

Important note

The filename can be anything, but we are keeping this as-is to be consistent with the Django convention.

Add the following code inside the backend/demo_app/ file:

from django.urls import path
from demo_app import views
urlpatterns = [
    path('hello-world/', views.hello_world)

Here, we are defining the URL pattern for the hello-world path, which links to the basic functional view we created earlier.

Opinionated note

We are using absolute import to import our demo_app views. This is a convention we shall follow throughout this book and we also recommend it for other projects. The advantage of using absolute import over relative import is that it is straightforward and clear to read. With just a glance, someone can easily tell what resource has been imported. Also, PEP-8 explicitly recommends using absolute imports.

Now, let’s connect the demo_app/ file to the main project config/ file:

from django.contrib import admin
from django.urls import include
from django.urls import path
urlpatterns = [
    path('demo-app/', include('demo_app.urls'))

Next, open in your browser to make sure our demo-app view is linked with Django. You should be able to see hello world displayed on the screen, just as we saw earlier in Figure 1.4.

So far, we have worked with plain vanilla Django, but now, we’ll see how we can integrate DRF into our project.

Integrating DRF

In the API-first world of development, where developers create APIs day in, day out for every feature they build, DRF is a powerful and flexible toolkit for building APIs using Django.

Important note

If you are not familiar with the basics of DRF, we will be going through the basics in this book. However, you can find more information here:

Now, let’s integrate DRF into our hello_world project. First, we need to install DRF in the virtual environment:

pip install djangorestframework

Now, go to and add rest_framework to INSTALLED_APPS. As you may recall, when we were integrating demo_app into the project, we mentioned that as the project grows, the INSTALLED_APPS list will also grow. To manage this better, we shall split our INSTALLED_APPS list into three sections:

  • DJANGO_APPS: This will contain a list of all the default Django apps and any new Django built-in apps we shall add to the project
  • THIRD_PARTY_APPS: Here, we shall maintain all the third-party apps we are integrating into the project, such as rest_framework
  • CUSTOM_APPS: We shall add all the apps we are creating for the project to this list – in our case, demo_app

Here is an example of how your INSTALLED_APPS list will look in the file:


So far, we have been using Django HttpResponse. Now, we shall integrate the DRF response into our view. So, go to the demo_app/ file and add the following code:

from rest_framework.decorators import api_view
from rest_framework.response import Response
def hello_world_drf(request, *args, **kwargs):
    return Response(data={'msg':'hello world'})

The integration of the DRF function-based view will change the UI completely for our endpoint. If you open, it will have a much more verbose UI, giving us a lot more information than before. This is particularly helpful when we start working with HTTP requests other than GET requests.

Here is our basic Django project integrated with DRF:

Figure 1.6: Using the DRF response in our hello-world-drf view

Figure 1.6: Using the DRF response in our hello-world-drf view

Now that we have a working project in Django, let’s learn some good practices that can help you go the extra mile without making mistakes yourself, rather than learning from the ones you have made earlier. DRF is the most popular Django package as it helps developers create REST endpoints. Now, let’s see what some good practices are for creating REST APIs.

Creating RESTful API endpoints with DRF

The most popular and widely used API is the REST API. Throughout this book, we shall be working with the REST API. REST has been around for more than two decades, and every company has its interpretation and implementation. In the following section, we shall try to put all the best practices used in the industry into practice.

Opinionated note

The RESTful API is not a protocol; instead, it is a standard convention that developers follow. There is no right or wrong while designing RESTful APIs. Since there is no enforced standard, the details we will provide are purely opinionated and come from my past experiences. You are free to pick the points you like and leave out the things that you feel are not relevant to your implementations.

Best practices for defining RESTful APIs

Let’s look at a few generic good practices that developers use in the industry while defining RESTful endpoints:

  • Using nouns instead of verbs in endpoint paths using appropriate HTTP request methods. Here are some examples (please note that the URL example used here is just an outline of how we should define our REST URLs and that we are not defining the exact code):
    # To get all blogs
    Avoid GET /get-all-blogs, rather use GET /blogs
    # To delete a particular blog
    Avoid POST /delete-blog rather use DELETE /blogs/<blogId>
    # To create a new blog with POST request
    Avoid POST /create-new-blog rather use POST /blogs
    # To update an existing blog with a PUT request
    Avoid PUT /update-blog rather use PUT /blogs/<blogId>
  • Using the appropriate HTTP method is preferred to perform CRUD operations. There are multiple HTTP methods present, but we shall only cover the top five commonly used methods:
    • GET: To retrieve an entity, be it a list or detail
    • POST: To create any new entity
    • PUT: To Update an entity
    • PATCH: To partially update an entity
    • DELETE: To delete an entity
  • It is preferred to create plural nouns in the endpoint. When you have to get a single entry, then use id after the endpoint to retrieve the information. For example, to get a list of blogs, use GET /blogs, and to get the details of one blog, use GET /blogs/<blog id>.
  • Using a logical nested structure for an endpoint is important to clean the API interface and maintain a good information architecture. For example, to get all the comments for a particular blog, the API should be GET /blogs/<blog id>/comments.
  • Versioning the API is important since it helps support legacy systems without breaking the contract in newer systems. Examples of this are /v1/blogs/ and /v2/blogs. We will learn more about this later, in the Using API versioning section.
  • Servers should send appropriate HTTP response status codes as per the action, along with the message body (if applicable). Here are a few of the most widely used HTTP status codes:
    • 2xx: Used for any success. For example, 200 is for any request responding with the data successfully, 201 is for creating a new entry, and so on.
    • 3xx: Used for any redirection.
    • 4xx: For any error. For example, use 400 for bad requests and 404 for requested data not found.
    • 5xx: When the server crashes due to any unexpected request or the server is unavailable.
  • The server must accept and respond with a JSON response. The API will not support other data types, such as plain text, XML, and others.

Best practices to create a REST API with DRF

DRF is a framework that helps us create the REST endpoint faster. It’s the responsibility of the developer to write scalable and maintainable code while following the best practices. Let’s look at some best practices that we can implement using DRF.

Using API versioning

Creating versions of an API is probably the most important thing to follow when working with clients whose updates are not under your control. An example of this is working on a mobile app. Once an end user installs a given mobile app, with a given API integrated, we have to support the given API until the end user updates the mobile app version with the newer API.

While creating an endpoint, a developer should consider all the future requirements possible, along with all the corner cases. However, just like it is not possible to predict the future, a developer cannot always foresee how the current API design might have to be redesigned. A redesign would mean breaking the contract between the client and the server. This is when the importance of API versioning comes into the picture. A well-versioned API will implement a new contract without breaking any of the existing clients.

There are multiple ways to implement API versioning:

  • Accept header versioning: Since the version is passed through the Accept header, whenever there is a new version, the client doesn’t need to update any endpoint whenever a new version is created:
    GET /bookings/ HTTP/1.1
    Accept: application/json; version=1.0
  • URL path versioning: The API version is passed through the URL path pattern. This is one of the most widely used API versioning methods due to it providing better visibility:
    GET /v1/bookings/ HTTP/1.1
    Accept: application/json
  • Query parameter versioning: The query parameter in the URL contains the version. After URL path-based versioning, this is the second most common versioning method due to its cleaner interface and better discoverability:
    GET /something/?version=1.0 HTTP/1.1
    Accept: application/json
  • Host name versioning: This involves using the subdomain to pass the API version in the hostname. This kind of versioning is used when someone migrates the whole service to a newer version rather than single endpoints:
    GET /bookings/ HTTP/1.1
    Accept: application/json

DRF supports all four methods of API versioning out of the box and also gives the option to create our custom API version logic if needed. We shall explore URLPathVersioning primarily since it is one of the easiest and most popular ways of implementing versioning using DRF.

In URL path versioning, the API version is passed in the URL path, which makes it easy to identify on both the client and server side. To integrate URLPathVersioning in DRF, add the following in the Django config/ file:

    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning'

Now, we must add the version to the URL path. It is important to name the URL parameter <version> since DRF is expecting it to be <version> by default. Here, <version> is the URL’s pattern, which means that any URL that matches this pattern shall be linked to the views.

Important note

To learn more about urlpatterns, go to

It is advisable to add <version> at the beginning of the URL, so let’s do that in the main config/ file:

urlpatterns = [
    path('<version>/demo-app-version/', include('demo_app.urls'))

Once we have configured the URL with the <version>, we can try to create a new view and retrieve the version in our view. Add the following code to your demo_app/ file:

from django.urls import path
from demo_app import views
urlpatterns = [
    path('hello-world/', views.hello_world),
    path('demo-version/', views.demo_version),

We shall retrieve the API version in the view and return the version in response:

def demo_version(request, *args, **kwargs):
    version = request.version
    return Response(data={
        'msg': f'You have hit {version} of demo-api'

Now, when we open, we should be able to see the following screen:

Figure 1.7: Output showing which version we have hit for the given API

Figure 1.7: Output showing which version we have hit for the given API

If we change the URL to, then we’ll see that it returns v9. v9 might not have been released yet, so this might create confusion for the end user hitting the endpoint. To solve this problem, we shall see how we can customize the version class of DRF so that we can add constraints that can help us design better applications.

Using a custom version class with DRF

Let’s see how we can extend the URLPathVersioning class provided by DRF to address the problem we just raised. First, create a file called demo_app/ This file will have a custom version class for each view, along with a default class for all the views that don’t have multiple versions yet:

from rest_framework.versioning import URLPathVersioning
class DefaultDemoAppVersion(URLPathVersioning):
    allowed_versions = ['v1']
    version_param = 'version'
class DemoViewVersion(DefaultDemoAppVersion):
    allowed_versions = ['v1', 'v2', 'v3']
class AnotherViewVersion(DefaultDemoAppVersion):
    allowed_versions = ['v1', 'v2']

Let’s see what the preceding code does:

  • The DefaultDemoAppVersion class can be used for all the views that are created in demo_app. It has an allowed_versions attribute that lists all the allowed versions that can be used in the URL path whenever we use this class-based view. version_param is the URL path parameter name that we have used to define the version; it can be anything, depending on how you name the parameter, but in our case, we are using <version>, which is used in the config/ file. This class will be used for all the views that are created in the demo app by default until a new version is added, after which we will create an individual class, as shown next.
  • The DemoViewVersion class will contain the list of all the allowed_versions attributes for DemoView that are allowed in the URL path.
  • The AnotherViewVersion class will contain all the versions that are allowed for a different class.

Add the following code to the demo_app/ file to integrate the custom version class (note that the custom versioning_class can be only linked to a class-based view, so we are using APIView here):

from rest_framework.response import Response
from rest_framework.views import APIView
from demo_app import custom_versions
class DemoView(APIView):
    versioning_class = custom_versions.DemoViewVersion
    def get(self, request, *args, **kwargs):
        version = request.version
        return Response(data={'msg': f' You have hit {version}'})
class AnotherView(APIView):
    versioning_class = custom_versions.AnotherViewVersion
    def get(self, request, *args, **kwargs):
        version = request.version
        if version == 'v1':
            # perform v1 related tasks
            return Response(data={'msg': 'v1 logic'})
        elif version == 'v2':
            # perform v2 related tasks
            return Response(data={'msg': 'v2 logic'})

Let’s explore the code and understand what is happening under the hood when we use the custom version class:

  • The DemoView class is a class-based APIView where we are passing the allowed versions for the view by the versioning_class attribute. This allows the request object to have a version attribute that is parsed from the URL path. Since we have specified the DemoViewVersion class, this view will only allow the v1, v2, and v3 versions in the URL path. Any other version in the path will result in a 404 response.
  • The AnotherView class is a class-based view where we are passing AnotherViewVersion as the versioning_class attribute. In this view, we are bifurcating the request by checking different versions and responding differently whenever we have a v1 or v2 request.

Now, to link the view logic to the demo_app/ file, add the following code:

urlpatterns = [
    path('hello-world/', views.hello_world),
    path('demo-version/', views.demo_version),
    path('custom-version/', views.DemoView.as_view()),
    path('another-custom-version/', views.AnotherView.as_view())

If we go to in our browser, we shall see the following error as shown in Figure 1.8, since we have only allowed three versions in our custom versioning_class:

Figure 1.8: 404 error message stating “Invalid version in URL path”

Figure 1.8: 404 error message stating “Invalid version in URL path”

This serves our purpose of only allowing certain versions of the API; any other API version shall result in an error response.

Important note

Custom versioning can only be attached to class-based views. If you don’t pass any custom versioning_class, then Django will pick DEFAULT_VERSIONING_CLASS from the default settings.

Avoid Router

Frameworks such as Ruby on Rails provide the functionality to automatically map requests to a given pattern of URLs, depending on the functionality. DRF borrowed this concept and incorporated it into the framework as the Routers feature. Though this is a wonderful concept to learn and experiment with, developers should avoid it in production since this goes against the principle of Django: “Explicit is better than implicit.”

Django mentions that it should not show too much of the magic. I have personally seen legacy systems where developers added Router and after a couple of months, when a different developer wanted to fix a bug in the view, they were unable to find the corresponding view directly before having the “aha!” moment of identifying the Router concept.

Opinionated note

Avoiding the use of Router is something I have learned the hard way and have seen multiple developers avoid in production. But this is also an opinion that was developed through a bad experience; you can always try to implement it in a better way in your project.

If you want to learn more about Router, you can do so here:

With that, we’ve learned how to create RESTful APIs and work with versioning. Now, let’s learn how to work with views using DRF. We mainly write business logic inside views.

Working with views using DRF

DRF extends the views concept of Django to provide a better interface for creating REST endpoints. Just like functional and class-based views in Django, DRF also supports both of them. However, it depends on the developer to choose which type of view fits their use case.

As a rule of thumb, I am always inclined toward functional views when I have a standalone endpoint where the logic is straightforward and won’t have any complexity in the future. Class-based views have a learning curve that stops developers from using them initially, but once the entry barrier is breached, developers rarely move back to functional views. Let’s explore both types of views in more detail.

Functional views

Django provides an easy functional view interface that helps any developer get going with faster development; that is why it is very popular for any developer starting Django. DRF keeps the simplicity of Django views and allows developers to convert a Django view into DRF using a simple decorator, @api_view. I prefer using it when the logic is straightforward or when I’m working on a simple project that will not get too complex.

DRF gives us @api_view to work with regular Django functional views. Adding the decorator converts the usual Django HttpRequest into a Request instance. The decorator takes a list of all the HTTP-allowed methods for the given functional view block; any method that is not listed will not be allowed. By default, if no HTTP method is mentioned, then it will only allow GET methods. For a response to views, we should use the Response class from DRF rather than HttpResponse from Django; it automatically takes care of bypassing cross-site request forgery (CSRF) for views and gives us a UI to interact with the backend service.

Let’s look at an example of a DRF functional view:

from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['GET', 'POST', 'PUT'])
def hello_world(request, *args, **kwargs):
    if request.method == 'POST':
        return Response(data={'msg': 'POST response block'})
    elif request.method == 'PUT':
        return Response(data={'msg': 'PUT response block'})
    return Response(data={'msg': 'GET response block'})

Now, let’s learn how to work with class-based views, which are widely used throughout the industry.

Class-based views

As mentioned previously, class-based views have a learning curve, so they’re avoided initially by a lot of developers. Even I avoided it for a couple of months until I saw the bigger picture, but ever since, there has been no looking back. It takes advantage of the inheritance property and helps implement the Don’t Repeat Yourself (DRY) principle. For large code bases, it is an absolute must, and I recommend that anyone starting a new project implement it from the start.

DRF provides two types of class-based views: APIView and Generic Views.


The DRF APIView class is an extension of Django’s View class. Using the APIView class converts the default Django HttpRequest into a Request object, and the handler methods can return DRF’s Response object rather than Django’s HttpResponse. It supports additional policy attributes such as authentication_classes, permission_classes, and versioning_classes, which make the life of a developer much easier. We shall use them in the following chapters and discuss them in detail.

The interface for using APIView is simple: to implement any HTTP request method for a given endpoint, simply create a new method in the class with the given name; for example, a GET request would have a get method, and a DELETE request would have a delete method, and so on. Whenever a request is hit for the given HTTP method, the corresponding method will be called automatically.

Let’s implement a quick example with APIView to learn more:

from rest_framework.views import APIView
class DemoAPIView(APIView):
    def get(self, request, *args, **kwargs):
        return Response(data={'msg': 'get request block'})
    def post(self, request, *args, **kwargs):
        return Response(data={'msg': 'post request block'})
    def delete(self, request, *args, **kwargs):
        return Response(data={'msg': 'delete request block'})

Linking an APIView implemented class to the file is different from how we linked functional views earlier. Instead, we use <class name>.as_view() to link it to the corresponding URL:

urlpatterns = [
    path('apiview-class/', views.DemoAPIView.as_view())

We shall expand our knowledge of APIView more as we learn about different concepts surrounding DRF and Django.

Generic Views

While building a web application, there comes a point when developers are doing the same monotonous job of writing repetitive logic. That is when the principle of DRY kicks in and the developer thinks about how to solve this repetitive pattern. The answer to this is Generic Views.

Generic Views is one of the most widely popular features of Django and DRF that helps developers build a basic CRUD operation API at lightning speed. Generic Views are tightly coupled with DRF’s serializer and model concepts, so we shall discuss Generic Views in detail once we learn about these concepts in the following chapters.

Opinionated note

We are not discussing Viewsets here since it is primarily used with Router. We have already discussed why we should avoid Router in Django, but if you are keen to learn more about it, you can go to the DRF documentation, which explains it quite well with relevant examples.

For more details about Viewsets, see

Now that we have seen the basic integration of DRF and how to work with views, let’s focus on how to improve the development experience. Web development is more than just writing code – it is also about using the right tools. Throughout this book, we shall introduce more and more tools for different purposes. For now, let’s learn about API development tools.

Introducing API development tools

While developing APIs, it is important to make sure the endpoint is working with every possible input and that we manually test the APIs before handing them over to the client team for integration. Though it is always favorable to write unit and integration tests for every piece of code/API you create, we might not be able to write them. Due to this, API development tools come in handy to support you through this journey.

Postman is one of the most popular tools in the developer community that helps developers build APIs at every stage of the development process. Though it is common to use Postman for testing APIs, it has far more use cases than just testing the APIs, as follows:

  • Creating mock servers with the API contract to help the client team work independently while the backend APIs are being developed
  • Creating documentation and OpenAPI specs for the team to work with endpoints
  • Creating a collection of APIs that can group APIs as per the use case
  • Creating tests that can run periodically on remote servers to make sure the contract of the APIs has not changed

The tutorial series on Postman’s website is quite intuitive: Please go through the tutorial if you are not familiar with it as we shall use Postman throughout this book to test our endpoints. You can use other tools for the same purpose, such as hoppscotch ( and testmace (

Learning new tools is a skill set that is important for the growth of any developer since these tools ease our development journey. Throughout this book, we shall introduce and learn about new tools that will help with just that.


In this chapter, we learned how to set up our local development environment with Python’s virtual environment and created a Django project from scratch. When we start a new project, the project structure is very important and, in this chapter, we have learned how to follow the best practices to create a new project structure for a Django web application.

Throughout this book, we will be working with REST APIs, so we provided a basic introduction to REST APIs and the best practices around how to create them, such as naming conventions, adding versioning, and using appropriate HTTP methods and status codes. We also learned how to integrate DRF with Django and create a basic demo app using URLs, functional views, and class-based views.

By reading this chapter, we should be comfortable with creating a Django web application from scratch with multiple Django apps, as well as URLs and views. We should be able to add Django apps to and create RESTful APIs using DRF.

So far, we have only worked with static responses without involving any databases. In the following chapter, we shall learn how to set up our database and connect a Postgres database to our Django project.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Gain a comprehensive understanding of the available deployment strategies for Django applications
  • Explore techniques and best practices to enhance the efficiency and performance of your Django application
  • Troubleshoot common production issues through efficient error logging and handling techniques
  • Purchase of the print or Kindle book includes a free PDF eBook


You may have got your first Django developer job after a six-week bootcamp or online course, and that’s great, but what’s next? In small companies, mentorship can be hard to come by and gaining the traits of a senior developer without that can take a long time. This is precisely where Django in Production comes into play. This book will first delve into the true meaning of "good practice" and help you understand the rationale behind industry professionals building websites in specific ways to develop a solid foundation for your Django projects. Next, you will uncover hidden Django secrets through hands-on exploration, leveraging the power of Docker and version control to your advantage. You will gain insights into mastering Git hooks for efficient code maintenance, establishing a robust CI pipeline, and harnessing the capabilities of AWS Beanstalk. These tools will empower you to develop highly scalable products—an essential skill set for aspiring developers transitioning from junior to senior roles. Later, you will understand the significance of monitoring and be introduced to industry-standard tools utilized by professionals for effective monitoring practices. By the end of this book, you will have set yourself apart from the crowd, equipped with the knowledge and expertise to thrive as a seasoned Django developer.

What you will learn

Write scalable and maintainable code like a Django expert Become proficient in Docker for Django and experience platform-agnostic development Explore intelligent practices for continuous integration Leverage the power of AWS to seamlessly deploy your application in a production environment Optimize unstable systems through effective performance monitoring Effortlessly handle authentication and authorization issues Automate repetitive tasks by creating custom middleware Thoroughly test your code using factory_boy and craft comprehensive API tests

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon AI Assistant (beta) to help accelerate your learning
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Buy Now

Product Details

Publication date : Apr 5, 2024
Length 348 pages
Edition : 1st Edition
Language : English
ISBN-13 : 9781804610480
Category :

Table of Contents

21 Chapters
Preface Chevron down icon Chevron up icon
1. Part 1 – Using Django and DRF to Build Modern Web Application Chevron down icon Chevron up icon
2. Chapter 1: Setting Up Django with DRF Chevron down icon Chevron up icon
3. Chapter 2: Exploring Django ORM, Models, and Migrations Chevron down icon Chevron up icon
4. Chapter 3: Serializing Data with DRF Chevron down icon Chevron up icon
5. Chapter 4: Exploring Django Admin and Management Commands Chevron down icon Chevron up icon
6. Chapter 5: Mastering Django Authentication and Authorization Chevron down icon Chevron up icon
7. Part 2 – Using the Advanced Concepts of Django Chevron down icon Chevron up icon
8. Chapter 6: Caching, Logging, and Throttling Chevron down icon Chevron up icon
9. Chapter 7: Using Pagination, Django Signals, and Custom Middleware Chevron down icon Chevron up icon
10. Chapter 8: Using Celery with Django Chevron down icon Chevron up icon
11. Chapter 9: Writing Tests in Django Chevron down icon Chevron up icon
12. Chapter 10: Exploring Conventions in Django Chevron down icon Chevron up icon
13. Part 3 – Dockerizing and Setting Up a CI Pipeline for Django Application Chevron down icon Chevron up icon
14. Chapter 11: Dockerizing Django Applications Chevron down icon Chevron up icon
15. Chapter 12: Working with Git and CI Pipelines Using Django Chevron down icon Chevron up icon
16. Part 4 – Deploying and Monitoring Django Applications in Production Chevron down icon Chevron up icon
17. Chapter 13: Deploying Django in AWS Chevron down icon Chevron up icon
18. Chapter 14: Monitoring Django Application Chevron down icon Chevron up icon
19. Index Chevron down icon Chevron up icon
20. Other Books You May Enjoy Chevron down icon Chevron up icon

Customer reviews

Filter icon Filter
Top Reviews
Rating distribution
Empty star icon Empty star icon Empty star icon Empty star icon Empty star icon 0
(0 Ratings)
5 star 0%
4 star 0%
3 star 0%
2 star 0%
1 star 0%

Filter reviews by

No reviews found
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial


How do I buy and download an eBook? Chevron down icon Chevron up icon

Where there is an eBook version of a title available, you can buy it from the book details for that title. Add either the standalone eBook or the eBook and print book bundle to your shopping cart. Your eBook will show in your cart as a product on its own. After completing checkout and payment in the normal way, you will receive your receipt on the screen containing a link to a personalised PDF download file. This link will remain active for 30 days. You can download backup copies of the file by logging in to your account at any time.

If you already have Adobe reader installed, then clicking on the link will download and open the PDF file directly. If you don't, then save the PDF file on your machine and download the Reader to view it.

Please Note: Packt eBooks are non-returnable and non-refundable.

Packt eBook and Licensing When you buy an eBook from Packt Publishing, completing your purchase means you accept the terms of our licence agreement. Please read the full text of the agreement. In it we have tried to balance the need for the ebook to be usable for you the reader with our needs to protect the rights of us as Publishers and of our authors. In summary, the agreement says:

  • You may make copies of your eBook for your own use onto any machine
  • You may not pass copies of the eBook on to anyone else
How can I make a purchase on your website? Chevron down icon Chevron up icon

If you want to purchase a video course, eBook or Bundle (Print+eBook) please follow below steps:

  1. Register on our website using your email address and the password.
  2. Search for the title by name or ISBN using the search option.
  3. Select the title you want to purchase.
  4. Choose the format you wish to purchase the title in; if you order the Print Book, you get a free eBook copy of the same title. 
  5. Proceed with the checkout process (payment to be made using Credit Card, Debit Cart, or PayPal)
Where can I access support around an eBook? Chevron down icon Chevron up icon
  • If you experience a problem with using or installing Adobe Reader, the contact Adobe directly.
  • To view the errata for the book, see and view the pages for the title you have.
  • To view your account details or to download a new copy of the book go to
  • To contact us directly if a problem is not resolved, use
What eBook formats do Packt support? Chevron down icon Chevron up icon

Our eBooks are currently available in a variety of formats such as PDF and ePubs. In the future, this may well change with trends and development in technology, but please note that our PDFs are not Adobe eBook Reader format, which has greater restrictions on security.

You will need to use Adobe Reader v9 or later in order to read Packt's PDF eBooks.

What are the benefits of eBooks? Chevron down icon Chevron up icon
  • You can get the information you need immediately
  • You can easily take them with you on a laptop
  • You can download them an unlimited number of times
  • You can print them out
  • They are copy-paste enabled
  • They are searchable
  • There is no password protection
  • They are lower price than print
  • They save resources and space
What is an eBook? Chevron down icon Chevron up icon

Packt eBooks are a complete electronic version of the print edition, available in PDF and ePub formats. Every piece of content down to the page numbering is the same. Because we save the costs of printing and shipping the book to you, we are able to offer eBooks at a lower cost than print editions.

When you have purchased an eBook, simply login to your account and click on the link in Your Download Area. We recommend you saving the file to your hard drive before opening it.

For optimal viewing of our eBooks, we recommend you download and install the free Adobe Reader version 9.