|$ curl https://forge-ai.dev/api/markdown?path=docs/python/django
$cat docs/django.md
updated Today·22-28 min read·published

Django

PythonDjangoWebIntermediate🎯Free Tools
Introduction

Django is the batteries-included web framework: ORM, admin, auth, templates, and migrations. Use it for full applications; prefer FastAPI for pure JSON APIs unless you need Django Admin/ORM ecosystem.

info

Create a project: django-admin startproject config . then python manage.py startapp shop.
Models
models.py
Python
1from django.db import models
2
3class Author(models.Model):
4 name = models.CharField(max_length=120)
5 email = models.EmailField(unique=True)
6
7 def __str__(self) -> str:
8 return self.name
9
10class Book(models.Model):
11 class Status(models.TextChoices):
12 DRAFT = "draft", "Draft"
13 PUB = "pub", "Published"
14
15 title = models.CharField(max_length=200)
16 author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name="books")
17 status = models.CharField(max_length=10, choices=Status.choices, default=Status.DRAFT)
18 published_at = models.DateTimeField(null=True, blank=True)
19 created_at = models.DateTimeField(auto_now_add=True)
20
21 class Meta:
22 indexes = [models.Index(fields=["status", "published_at"])]
23 ordering = ["-created_at"]
ORM Queries
orm.py
Python
1# from shop.models import Author, Book
2# Book.objects.filter(status=Book.Status.PUB).select_related("author")
3# Author.objects.prefetch_related("books").all()
4# Book.objects.filter(title__icontains="python").count()
5# Book.objects.create(title="X", author=author)
6#
7# from django.db.models import Count, Q
8# Author.objects.annotate(n=Count("books")).filter(n__gte=1)
9# Book.objects.filter(Q(status="pub") | Q(title__startswith="A"))

Prefer select_related for FK/OneToOne and prefetch_related for reverse/M2M to avoid N+1 queries.

Views — FBV & CBV
views.py
Python
1from django.http import JsonResponse, HttpRequest, HttpResponse
2from django.shortcuts import get_object_or_404, render
3from django.views import View
4from .models import Book
5
6def book_list(request: HttpRequest) -> HttpResponse:
7 books = Book.objects.select_related("author")[:50]
8 return render(request, "shop/book_list.html", {"books": books})
9
10def book_detail_api(request: HttpRequest, pk: int) -> JsonResponse:
11 book = get_object_or_404(Book.objects.select_related("author"), pk=pk)
12 return JsonResponse({"id": book.pk, "title": book.title, "author": book.author.name})
13
14class BookCreateView(View):
15 def post(self, request: HttpRequest) -> JsonResponse:
16 # validate POST / JSON then Book.objects.create(...)
17 return JsonResponse({"ok": True}, status=201)
URLconf
urls.py
Python
1from django.urls import path
2from . import views
3
4app_name = "shop"
5urlpatterns = [
6 path("books/", views.book_list, name="book-list"),
7 path("api/books/<int:pk>/", views.book_detail_api, name="book-api"),
8]
Migrations
migrations.sh
Bash
1python manage.py makemigrations
2python manage.py migrate
3python manage.py showmigrations
4python manage.py sqlmigrate shop 0001

best practice

Never edit applied migrations in shared branches — add a new migration instead.
Admin
admin.py
Python
1from django.contrib import admin
2from .models import Author, Book
3
4@admin.register(Author)
5class AuthorAdmin(admin.ModelAdmin):
6 list_display = ("name", "email")
7 search_fields = ("name", "email")
8
9@admin.register(Book)
10class BookAdmin(admin.ModelAdmin):
11 list_display = ("title", "author", "status", "published_at")
12 list_filter = ("status",)
13 autocomplete_fields = ("author",)
14 prepopulated_fields = {"title": ("title",)} # example; usually slug
Django REST Framework Overview

DRF adds serializers, viewsets, and browsable APIs on top of Django.

drf.py
Python
1# pip install djangorestframework
2from rest_framework import serializers, viewsets
3from .models import Book
4
5class BookSerializer(serializers.ModelSerializer):
6 class Meta:
7 model = Book
8 fields = ("id", "title", "status", "author")
9
10class BookViewSet(viewsets.ModelViewSet):
11 queryset = Book.objects.select_related("author").all()
12 serializer_class = BookSerializer
13
14# router.register("books", BookViewSet)
Settings Patterns
settings_snip.py
Python
1# config/settings.py (excerpt)
2from pathlib import Path
3import os
4
5BASE_DIR = Path(__file__).resolve().parent.parent
6SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
7DEBUG = os.getenv("DJANGO_DEBUG", "0") == "1"
8ALLOWED_HOSTS = os.getenv("DJANGO_ALLOWED_HOSTS", "localhost").split(",")
9DATABASES = {
10 "default": {
11 "ENGINE": "django.db.backends.postgresql",
12 "NAME": os.getenv("POSTGRES_DB", "app"),
13 "USER": os.getenv("POSTGRES_USER", "app"),
14 "PASSWORD": os.getenv("POSTGRES_PASSWORD", ""),
15 "HOST": os.getenv("POSTGRES_HOST", "localhost"),
16 "PORT": os.getenv("POSTGRES_PORT", "5432"),
17 }
18}
Django vs FastAPI
NeedPrefer
Admin + CMS-like CRUDDjango
Async JSON microserviceFastAPI
Batteries-included auth/sessionsDjango
OpenAPI-first micro APIsFastAPI
Complex relational domain + formsDjango
Forms (brief)
forms.py
Python
1from django import forms
2from .models import Book
3
4class BookForm(forms.ModelForm):
5 class Meta:
6 model = Book
7 fields = ("title", "author", "status")
8
9# if request.method == "POST":
10# form = BookForm(request.POST)
11# if form.is_valid():
12# form.save()
Testing
test_django.py
Python
1from django.test import TestCase, Client
2from shop.models import Author, Book
3
4class BookTests(TestCase):
5 def setUp(self):
6 self.author = Author.objects.create(name="Ada", email="a@e.com")
7 self.book = Book.objects.create(title="Notes", author=self.author)
8
9 def test_list(self):
10 c = Client()
11 r = c.get("/books/")
12 self.assertEqual(r.status_code, 200)
Middleware & Security
  • Keep DEBUG=False in production
  • Set SECURE_PROXY_SSL_HEADER / CSRF_TRUSTED_ORIGINS behind proxies
  • Use django-environ or pydantic-settings for secrets
  • Prefer get_object_or_404 over raw try/except DoesNotExist in views
Signals (use sparingly)
signals.py
Python
1from django.db.models.signals import post_save
2from django.dispatch import receiver
3from .models import Book
4
5@receiver(post_save, sender=Book)
6def book_saved(sender, instance: Book, created: bool, **kwargs):
7 if created:
8 print("new book", instance.pk)

warning

Signals hide control flow. Prefer explicit service functions when possible.
Auth Overview
auth.py
Python
1from django.contrib.auth.decorators import login_required
2from django.contrib.auth.mixins import LoginRequiredMixin
3from django.views.generic import ListView
4from .models import Book
5
6@login_required
7def private(request):
8 return render(request, "private.html")
9
10class MyBooks(LoginRequiredMixin, ListView):
11 model = Book
12 template_name = "my_books.html"
Management Commands
command.py
Python
1from django.core.management.base import BaseCommand
2from shop.models import Book
3
4class Command(BaseCommand):
5 help = "List books"
6
7 def add_arguments(self, parser):
8 parser.add_argument("--status", default="pub")
9
10 def handle(self, *args, **options):
11 qs = Book.objects.filter(status=options["status"])
12 for b in qs:
13 self.stdout.write(b.title)
Production Checklist
  • DEBUG=False, SECRET_KEY from env
  • ALLOWED_HOSTS / CSRF_TRUSTED_ORIGINS set
  • Migrations applied in deploy
  • Static files via WhiteNoise or CDN
  • CONN_MAX_AGE for DB pooling where appropriate
$Blueprint — Engineering Documentation·Section ID: PYTHON-DJANGO·Revision: 1.0

Community

Get help on Slack, Discord or VIP

Stuck on a guide? Join the community and ask.