DeveloperBootstrap

Python

Django Complete Guide

Master Django web development from setup to production deployment including models, views, templates, authentication, REST APIs, testing, performance optimisation, and enterprise-grade best practices.

What you'll learn

  • Structure a Django project following best practices
  • Design models with the Django ORM
  • Build class-based views and REST APIs with DRF
  • Implement Django authentication and permissions
  • Write unit and integration tests with pytest-django
  • Optimise database queries and cache with Redis
  • Deploy Django to production with gunicorn and Nginx

Project Structure

Bash
1pip install django==5.0 djangorestframework psycopg2-binary
2django-admin startproject config .
3python manage.py startapp courses
4
5# Recommended layout
6myproject/
7├── config/          # settings, urls, wsgi, asgi
8├── courses/         # domain app
9│   ├── models.py
10│   ├── serializers.py
11│   ├── views.py
12│   ├── urls.py
13│   └── tests/
14├── users/
15├── requirements/
16│   ├── base.txt
17│   ├── development.txt
18│   └── production.txt
19└── manage.py

Models & ORM

Python
1# courses/models.py
2from django.db import models
3from django.contrib.auth import get_user_model
4
5User = get_user_model()
6
7class Course(models.Model):
8    title       = models.CharField(max_length=255, db_index=True)
9    description = models.TextField(blank=True)
10    author      = models.ForeignKey(User, on_delete=models.CASCADE, related_name='courses')
11    price       = models.DecimalField(max_digits=10, decimal_places=2, default=0)
12    created_at  = models.DateTimeField(auto_now_add=True)
13    updated_at  = models.DateTimeField(auto_now=True)
14    is_published = models.BooleanField(default=False)
15
16    class Meta:
17        ordering = ['-created_at']
18        indexes  = [models.Index(fields=['author', 'is_published'])]
19
20    def __str__(self) -> str:
21        return self.title
22
23# Efficient queries — avoid N+1
24courses = (
25    Course.objects
26    .select_related('author')
27    .prefetch_related('modules')
28    .filter(is_published=True)
29    .only('id', 'title', 'author__name', 'price')
30)

Views & URLs

Python
1# courses/views.py
2from django.views.generic import ListView, DetailView
3from .models import Course
4
5class PublishedCourseListView(ListView):
6    model = Course
7    template_name = 'courses/list.html'
8    context_object_name = 'courses'
9    paginate_by = 20
10
11    def get_queryset(self):
12        return Course.objects.filter(is_published=True).select_related('author')
13
14# courses/urls.py
15from django.urls import path
16from . import views
17
18urlpatterns = [
19    path('', views.PublishedCourseListView.as_view(), name='course-list'),
20    path('<int:pk>/', views.CourseDetailView.as_view(), name='course-detail'),
21]

Django REST Framework

Python
1# courses/serializers.py
2from rest_framework import serializers
3from .models import Course
4
5class CourseSerializer(serializers.ModelSerializer):
6    author_name = serializers.CharField(source='author.get_full_name', read_only=True)
7
8    class Meta:
9        model  = Course
10        fields = ['id', 'title', 'description', 'author_name', 'price', 'created_at']
11        read_only_fields = ['id', 'author_name', 'created_at']
12
13# courses/views.py (API)
14from rest_framework import viewsets, permissions, filters
15from django_filters.rest_framework import DjangoFilterBackend
16
17class CourseViewSet(viewsets.ModelViewSet):
18    queryset           = Course.objects.filter(is_published=True).select_related('author')
19    serializer_class   = CourseSerializer
20    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
21    filter_backends    = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
22    filterset_fields   = ['author']
23    search_fields      = ['title', 'description']
24    ordering_fields    = ['created_at', 'price']
25
26    def perform_create(self, serializer):
27        serializer.save(author=self.request.user)

Authentication & Permissions

Python
1pip install djangorestframework-simplejwt
2
3# config/settings.py
4REST_FRAMEWORK = {
5    'DEFAULT_AUTHENTICATION_CLASSES': [
6        'rest_framework_simplejwt.authentication.JWTAuthentication',
7    ],
8}
9
10# courses/permissions.py
11from rest_framework.permissions import BasePermission
12
13class IsOwnerOrReadOnly(BasePermission):
14    def has_object_permission(self, request, view, obj):
15        if request.method in ('GET', 'HEAD', 'OPTIONS'):
16            return True
17        return obj.author == request.user

Testing

Python
1pip install pytest pytest-django pytest-cov
2
3# pytest.ini
4[pytest]
5DJANGO_SETTINGS_MODULE = config.settings.test
6addopts = --cov=. --cov-report=term-missing --cov-fail-under=80
7
8# courses/tests/test_api.py
9import pytest
10from rest_framework.test import APIClient
11from courses.models import Course
12
13@pytest.fixture
14def client(): return APIClient()
15
16@pytest.fixture
17def user(db, django_user_model):
18    return django_user_model.objects.create_user(username='alice', password='pass')
19
20@pytest.mark.django_db
21def test_list_courses(client, user):
22    Course.objects.create(title='Django 101', author=user, is_published=True)
23    response = client.get('/api/v1/courses/')
24    assert response.status_code == 200
25    assert len(response.data['results']) == 1

Performance & Caching

Python
1pip install django-redis
2
3# settings.py
4CACHES = {
5    'default': {
6        'BACKEND':  'django_redis.cache.RedisCache',
7        'LOCATION': 'redis://127.0.0.1:6379/1',
8        'OPTIONS':  { 'CLIENT_CLASS': 'django_redis.client.DefaultClient' },
9        'TIMEOUT':  300,
10    }
11}
12
13# Cache a view for 5 minutes
14from django.views.decorators.cache import cache_page
15
16@cache_page(60 * 5)
17def course_list(request):
18    ...

Production Deployment

Bash
1pip install gunicorn whitenoise
2
3# gunicorn.conf.py
4workers   = 4
5bind      = '0.0.0.0:8000'
6accesslog = '-'
7errorlog  = '-'
8
9# Collect static files
10python manage.py collectstatic --noinput
11
12# Nginx upstream
13upstream django {
14    server 127.0.0.1:8000;
15}
16
17server {
18    location / { proxy_pass http://django; }
19    location /static/ { alias /app/staticfiles/; expires 1y; }
20}