Skip to content
Django Advanced — REST APIs, Auth, Celery, Caching & Deployment Guide

Django Advanced — REST APIs, Auth, Celery, Caching & Deployment Guide

DodaTech Updated Jun 6, 2026 8 min read

Django’s advanced features — REST APIs, asynchronous tasks, caching, and production deployment — transform a simple web application into a scalable, production-ready system.

What You’ll Learn

  • How to build REST APIs using Django REST Framework
  • How to implement JWT authentication for secure API access
  • How to run background tasks with Celery
  • How to cache database queries and rendered pages
  • How to write unit tests and functional tests
  • How to deploy Django to production with Gunicorn and Nginx

Why Advanced Django Matters

Building a web app is one thing. Building one that can handle thousands of users, process background tasks, and stay secure is another. Durga Antivirus Pro uses Django to serve real-time threat intelligence, process malware analysis results in the background, and cache frequently accessed threat signatures. DodaZIP uses Django REST Framework for its API endpoints. Understanding advanced Django patterns lets you build professional-grade applications.

    flowchart LR
    A["Django Basics"] --> B["REST APIs"]
    A --> C["Auth & Security"]
    A --> D["Background Tasks"]
    A --> E["Caching"]
    A --> F["Testing"]
    A --> G["Deployment"]
    B --> H["Production App"]
    C --> H
    D --> H
    E --> H
    F --> H
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style F fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style G fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style H fill:#2563eb,stroke:#2563eb,color:#fff
  
Prerequisite: You should understand Django — models, views, templates, and URLs. Familiarity with REST API concepts and Python is required.

Django REST Framework

DRF is the standard way to build REST APIs with Django. It handles serialization, authentication, and view sets.

Serializers and ViewSets

from rest_framework import serializers, viewsets
from rest_framework.permissions import IsAuthenticated
from .models import Product

# Serializer — converts model instances to JSON and back
class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ["id", "name", "price", "stock"]

# ViewSet — handles all CRUD operations automatically
class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    permission_classes = [IsAuthenticated]

Let’s break this down:

  • ModelSerializer automatically generates fields from the model
  • ModelViewSet provides list, create, retrieve, update, partial_update, destroy actions
  • IsAuthenticated ensures only authenticated users can access the API

URL Routing

# urls.py
from rest_framework.routers import DefaultRouter
from .views import ProductViewSet

router = DefaultRouter()
router.register(r"products", ProductViewSet)

urlpatterns = router.urls
# Generates:
# GET /products/ — list
# POST /products/ — create
# GET /products/{id}/ — retrieve
# PUT /products/{id}/ — update
# DELETE /products/{id}/ — delete

Authentication with JWT

JWT (JSON Web Tokens) provide stateless authentication — the client sends a token with each request instead of maintaining a session on the server.

# settings.py
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ),
}

# urls.py
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

urlpatterns = [
    path("api/token/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
    path("api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
]

Celery for Async Tasks

Celery runs tasks in the background, freeing your web server to handle requests quickly:

# tasks.py
from celery import shared_task
from django.core.mail import send_mail

@shared_task
def send_welcome_email(user_id):
    """Send welcome email asynchronously."""
    from django.contrib.auth.models import User
    user = User.objects.get(id=user_id)
    send_mail(
        "Welcome!",
        f"Hi {user.username}, thanks for joining!",
        "noreply@example.com",
        [user.email],
        fail_silently=False,
    )

# Call it from a view:
# send_welcome_email.delay(user.id)

Caching

Caching stores expensive computation results so they don’t need to be recomputed:

from django.core.cache import cache
from django.views.decorators.cache import cache_page

# Cache a view for 15 minutes
@cache_page(60 * 15)
def product_list(request):
    products = Product.objects.all()
    return render(request, "products.html", {"products": products})

# Cache a specific query result
def expensive_report(request):
    report = cache.get_or_set(
        "daily_report",
        generate_report(),  # Expensive computation
        3600  # Cache for 1 hour
    )
    return render(request, "report.html", {"report": report})

Testing Django Applications

from django.test import TestCase, Client
from django.urls import reverse
from .models import Product

class ProductTest(TestCase):
    def setUp(self):
        self.client = Client()
        self.product = Product.objects.create(
            name="Test Widget",
            price=10.99,
            stock=100
        )

    def test_product_list_view(self):
        response = self.client.get(reverse("product-list"))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Test Widget")

    def test_product_creation(self):
        response = self.client.post(reverse("product-list"), {
            "name": "New Product",
            "price": 5.99,
            "stock": 50
        })
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Product.objects.count(), 2)

Common Mistakes

1. Forgetting Database Indexes

For frequently queried fields, add db_index=True or Meta.indexes. Without indexes, queries slow down as data grows.

2. N+1 Query Problem

# Bad — N queries (one for each author's books)
authors = Author.objects.all()
for author in authors:
    books = author.books.all()  # Extra query each time!

# Good — 2 queries with prefetch_related
authors = Author.objects.prefetch_related("books").all()

3. DEBUG = True in Production

Never deploy with DEBUG = True. It exposes sensitive error information to users.

4. Not Using Environment Variables for Secrets

Hardcoding SECRET_KEY, database passwords, and API keys in settings.py is a security risk. Use environment variables.

Production Checklist

  • Set DEBUG = False
  • Use environment variables for secrets
  • Configure a production database (PostgreSQL)
  • Set up static/media file serving (WhiteNoise or CDN)
  • Enable HTTPS with a reverse proxy (Nginx)
  • Use Gunicorn as the WSGI server
  • Set up logging and error monitoring (Sentry)

Practice Questions

1. What is the role of a serializer in Django REST Framework?

A serializer converts complex data types (model instances, querysets) to JSON for API responses and validates incoming JSON data before saving to the database. ModelSerializer automatically generates fields and validation from a model definition.

2. How does JWT authentication differ from session-based authentication?

JWT is stateless — the server doesn’t store session data. The client sends a self-contained token with each request. Session auth stores session IDs on the server and uses cookies. JWT is preferred for mobile apps and microservices; sessions are simpler for traditional web apps.

3. When should you use Celery instead of Django’s built-in async views?

Use Celery for long-running background tasks like sending emails, generating PDF reports, or processing file uploads. Django’s built-in async views are better for I/O-bound operations (multiple database queries, external API calls) that complete within a request lifecycle.

4. What is the N+1 query problem and how do you fix it in Django?

The N+1 problem occurs when a query fetches related objects one by one in a loop, causing N extra queries for N items. Fix it with select_related() (for ForeignKey, OneToOneField — SQL JOIN) or prefetch_related() (for ManyToManyField, reverse relations — separate query + Python join).

5. Challenge: Add caching + pagination to a DRF view.

from rest_framework.pagination import PageNumberPagination
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator

class ProductPagination(PageNumberPagination):
    page_size = 20
    page_size_query_param = "page_size"

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    pagination_class = ProductPagination

    @method_decorator(cache_page(60 * 15))
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

This paginates results (20 per page) and caches the list response for 15 minutes, reducing database load on frequently accessed endpoints.

Mini Project: Build a Task Management API

Build a complete task management REST API using the patterns from this tutorial:

Step 1: Setup and Model

django-admin startproject taskmanager
cd taskmanager
python manage.py startapp tasks
pip install djangorestframework djangorestframework-simplejwt celery django-redis
# tasks/models.py
from django.db import models

class Task(models.Model):
    PRIORITY_CHOICES = [("low", "Low"), ("medium", "Medium"), ("high", "High")]
    STATUS_CHOICES = [("todo", "To Do"), ("in_progress", "In Progress"), ("done", "Done")]

    title = models.CharField(max_length=200)
    description = models.TextField(blank=True)
    priority = models.CharField(max_length=10, choices=PRIORITY_CHOICES, default="medium")
    status = models.CharField(max_length=15, choices=STATUS_CHOICES, default="todo")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-created_at"]

Step 2: Serializer and ViewSet

# tasks/serializers.py
from rest_framework import serializers
from .models import Task

class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = "__all__"
        read_only_fields = ["created_at", "updated_at"]

# tasks/views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from .models import Task
from .serializers import TaskSerializer

class TaskViewSet(viewsets.ModelViewSet):
    queryset = Task.objects.all()
    serializer_class = TaskSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        return Task.objects.filter(
            title__icontains=self.request.query_params.get("search", "")
        )

Step 3: URL Routing and JWT Auth

# tasks/urls.py
from rest_framework.routers import DefaultRouter
from .views import TaskViewSet

router = DefaultRouter()
router.register(r"tasks", TaskViewSet)
urlpatterns = router.urls

# taskmanager/urls.py
from django.urls import path, include
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

urlpatterns = [
    path("api/", include("tasks.urls")),
    path("api/token/", TokenObtainPairView.as_view()),
    path("api/token/refresh/", TokenRefreshView.as_view()),
]

Step 4: Background Task with Celery

# tasks/tasks.py
from celery import shared_task
from django.core.mail import send_mail

@shared_task
def send_task_notification(task_id):
    from .models import Task
    task = Task.objects.get(id=task_id)
    send_mail(
        f"Task Updated: {task.title}",
        f"Task '{task.title}' is now {task.status}",
        "noreply@taskmanager.com",
        ["admin@example.com"],
    )

# In your view, after saving:
# send_task_notification.delay(task.id)

Step 5: Test It

# Terminal 1 — start Redis
redis-server

# Terminal 2 — start Celery worker
celery -A taskmanager worker -l info

# Terminal 3 — start Django
python manage.py runserver

# Get a token
curl -X POST http://localhost:8000/api/token/ \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"yourpassword"}'

# Create a task
curl -X POST http://localhost:8000/api/tasks/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"title":"Learn Django REST","priority":"high"}'

Expected output for the create request:

{"id":1,"title":"Learn Django REST","description":"","priority":"high","status":"todo","created_at":"...","updated_at":"..."}

Challenge extension: Add filtering by status/priority, implement pagination (20 per page), and cache the list endpoint for 5 minutes.

FAQ

What's the difference between Django REST Framework and plain Django?
DRF is a library built on top of Django specifically for building REST APIs. It provides serializers, authentication classes, view sets, and browsable API pages that plain Django doesn’t have.
When should I use Celery vs Django's built-in async?
Use Celery for long-running background tasks (email, report generation). Django’s built-in async (views, ORM) is better for I/O-bound operations like database queries.
Is JWT better than session authentication for APIs?
JWT is stateless and works well for mobile apps and microservices. Session auth is simpler for traditional web apps. For APIs consumed by mobile apps and third parties, JWT is typically preferred.

Try It Yourself

Create a Product model, a DRF ModelViewSet, and register it with a router. Run python manage.py runserver and visit /api/products/ — you should see the browsable API.

What’s Next

Now that you’ve mastered advanced Django, explore other Python frameworks.

TopicDescriptionLink
Django ReferenceComplete Django API referencehttps://tutorials.dodatech.com/programming-languages/python/django/reference/
FastAPI FrameworkModern async Python frameworkhttps://tutorials.dodatech.com/programming-languages/python/fastapi/reference/
Flask FrameworkLightweight Python web frameworkhttps://tutorials.dodatech.com/programming-languages/python/flask/reference/
REST APILearn REST API design principlesREST API

What’s Next

Congratulations on completing this Advanced tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro