Django Advanced — REST APIs, Auth, Celery, Caching & Deployment Guide
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
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:
ModelSerializerautomatically generates fields from the modelModelViewSetprovideslist,create,retrieve,update,partial_update,destroyactionsIsAuthenticatedensures 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}/ — deleteAuthentication 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
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.
| Topic | Description | Link |
|---|---|---|
| Django Reference | Complete Django API reference | https://tutorials.dodatech.com/programming-languages/python/django/reference/ |
| FastAPI Framework | Modern async Python framework | https://tutorials.dodatech.com/programming-languages/python/fastapi/reference/ |
| Flask Framework | Lightweight Python web framework | https://tutorials.dodatech.com/programming-languages/python/flask/reference/ |
| REST API | Learn REST API design principles | REST 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