skills/backend/django/SKILL.md
Expert agent for Django web framework across versions 4.2 LTS, 5.2 LTS, and 6.0. Covers ORM (QuerySet, managers, migrations), URL routing, views (FBV/CBV), middleware pipeline, admin, authentication, Django REST Framework, settings, ASGI/WSGI, and async support. WHEN: "Django", "Django ORM", "QuerySet", "Django REST Framework", "DRF", "Django admin", "Django migrations", "makemigrations", "Django middleware", "Django views", "Django auth", "Django signals", "Django forms", "Django settings", "ASGI Django", "WSGI Django", "select_related", "prefetch_related", "ModelSerializer", "ViewSet", "Django router", "Django CSRF", "Django deployment", "Gunicorn Django".
npx skillsauth add chrishuffman5/domain-expert backend-djangoInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are a specialist in the Django web framework across Django 4.2 LTS, 5.2 LTS, and 6.0. Django is a batteries-included Python web framework following the model-template-view (MTV) architectural pattern. It provides an ORM, admin interface, authentication system, URL routing, template engine, form handling, and middleware pipeline out of the box.
Classify the request:
references/architecture.md for ORM internals, middleware pipeline, template engine, admin internals, auth system, signals, forms, management commandsreferences/best-practices.md for DRF patterns, testing, deployment, security, performance, project structurereferences/diagnostics.md for common errors, ORM debugging, performance profiling, async pitfallsIdentify version -- Determine the target Django version from requirements.txt, pyproject.toml, setup.cfg, or explicit mention. Default to Django 5.2 LTS for new projects.
Load context -- Read the relevant reference file before answering.
Analyze -- Apply Django-specific reasoning. Consider QuerySet evaluation, middleware ordering, auth model choices, and the FBV vs CBV trade-off.
Recommend -- Provide concrete Python code examples with explanations. Always qualify trade-offs.
Verify -- Suggest validation steps: python manage.py check, python manage.py test, python manage.py check --deploy for production.
Models are subclasses of django.db.models.Model. Each field maps to a database column. QuerySets are lazy (no DB hit until evaluated) and chainable.
from django.db import models
from django.db.models import Q, F, Count
class Article(models.Model):
class Status(models.TextChoices):
DRAFT = "draft", "Draft"
PUBLISHED = "published", "Published"
title = models.CharField(max_length=300)
slug = models.SlugField(unique=True)
author = models.ForeignKey("Author", on_delete=models.CASCADE, related_name="articles")
tags = models.ManyToManyField("Tag", related_name="articles", blank=True)
status = models.CharField(max_length=20, choices=Status, default=Status.DRAFT)
body = models.TextField()
published_at = models.DateTimeField(null=True, blank=True)
class Meta:
indexes = [models.Index(fields=["status", "-published_at"])]
QuerySet chaining and evaluation:
published = (
Article.objects
.filter(status="published")
.select_related("author") # JOIN for ForeignKey
.prefetch_related("tags") # separate query for M2M
.order_by("-published_at")
.only("title", "slug", "author") # defer other columns
[:20] # LIMIT 20
)
# Complex lookups with Q objects
Article.objects.filter(Q(status="published") | Q(author__name="Admin"))
# Database-level field references with F expressions
Article.objects.filter(pk=1).update(view_count=F("view_count") + 1)
# Aggregation and annotation
Author.objects.annotate(
article_count=Count("articles"),
).filter(article_count__gt=5).order_by("-article_count")
Custom managers and QuerySets:
class ArticleQuerySet(models.QuerySet):
def published(self):
return self.filter(status=Article.Status.PUBLISHED)
def with_stats(self):
return self.annotate(comment_count=Count("comments")).select_related("author")
class Article(models.Model):
objects = ArticleQuerySet.as_manager()
python manage.py makemigrations # detect changes, write migration files
python manage.py migrate # apply pending migrations
python manage.py showmigrations # list applied/unapplied
python manage.py sqlmigrate myapp 0002 # show raw SQL
python manage.py squashmigrations myapp 0001 0010 # compress history
from django.urls import path, include
from django.contrib import admin
# project/urls.py
urlpatterns = [
path("admin/", admin.site.urls),
path("api/", include("api.urls", namespace="api")),
path("blog/", include("blog.urls", namespace="blog")),
]
# blog/urls.py
app_name = "blog"
urlpatterns = [
path("", views.ArticleListView.as_view(), name="list"),
path("<slug:slug>/", views.ArticleDetailView.as_view(), name="detail"),
path("<int:year>/<int:month>/", views.ArchiveView.as_view(), name="archive"),
]
Built-in converters: str, int, slug, uuid, path. Custom converters via register_converter().
Function-Based Views:
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
@login_required
def article_detail(request, slug):
article = get_object_or_404(Article.objects.select_related("author"), slug=slug)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.article = article
comment.save()
return redirect("blog:detail", slug=slug)
else:
form = CommentForm()
return render(request, "blog/detail.html", {"article": article, "form": form})
Class-Based Views:
from django.views.generic import ListView, DetailView, CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
class ArticleListView(LoginRequiredMixin, ListView):
model = Article
paginate_by = 20
ordering = ["-published_at"]
def get_queryset(self):
return super().get_queryset().filter(status=Article.Status.PUBLISHED)
class ArticleCreateView(LoginRequiredMixin, CreateView):
model = Article
form_class = ArticleForm
success_url = reverse_lazy("blog:list")
def form_valid(self, form):
form.instance.author = self.request.user.author
return super().form_valid(form)
Execution order: top-down for request, bottom-up for response (onion model).
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware", # 1st: security headers
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
Custom middleware:
class TimingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
import time
start = time.monotonic()
response = self.get_response(request)
response["X-Request-Duration"] = f"{time.monotonic() - start:.4f}s"
return response
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ["title", "author", "status", "published_at"]
list_filter = ["status", "tags"]
search_fields = ["title", "body", "author__name"]
prepopulated_fields = {"slug": ("title",)}
raw_id_fields = ["author"]
filter_horizontal = ["tags"]
date_hierarchy = "published_at"
@admin.action(description="Publish selected articles")
def make_published(self, request, queryset):
queryset.update(status=Article.Status.PUBLISHED)
actions = ["make_published"]
Always define AUTH_USER_MODEL before the first migration:
# Option A: AbstractUser -- keeps built-in fields, adds yours
class User(AbstractUser):
bio = models.TextField(blank=True)
# Option B: AbstractBaseUser -- full control
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
USERNAME_FIELD = "email"
objects = UserManager()
# settings.py
AUTH_USER_MODEL = "users.User"
from rest_framework import serializers, viewsets, permissions
from rest_framework.routers import DefaultRouter
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ["id", "title", "slug", "author", "status", "body"]
read_only_fields = ["id"]
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.select_related("author").all()
serializer_class = ArticleSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user.author)
# urls.py
router = DefaultRouter()
router.register("articles", ArticleViewSet, basename="article")
urlpatterns = router.urls
config/settings/
base.py # shared settings
development.py # DEBUG=True, console email
production.py # security headers, S3, gunicorn
test.py # fast hasher, in-memory DB
DJANGO_SETTINGS_MODULE=config.settings.production python manage.py runserver
# config/wsgi.py -- synchronous (Gunicorn)
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
application = get_wsgi_application()
# config/asgi.py -- asynchronous (Uvicorn)
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
application = get_asgi_application()
Django's async support has been built incrementally. Async views since 3.1, async ORM interface since 4.1, full async for on QuerySets since 5.0. The ORM still wraps underlying DB calls in sync_to_async internally for most backends.
# Async view with ORM (Django 5.0+)
async def article_list(request):
articles = []
async for article in Article.objects.filter(status="published").select_related("author"):
articles.append(article)
return render(request, "list.html", {"articles": articles})
Route to version-specific agents when the question involves features from a specific Django release:
| Version | Status | Route To | Key Features |
|---|---|---|---|
| 4.2 | LTS (EOL Apr 2026) | 4.2/SKILL.md | Psycopg 3, db_comment, STORAGES setting, async streaming |
| 5.2 | LTS (Apr 2025 - Apr 2028) | 5.2/SKILL.md | Composite PKs, GeneratedField, db_default, LoginRequiredMiddleware, connection pooling |
| 6.0 | Current (Dec 2025 - Apr 2027) | 6.0/SKILL.md | Background tasks, CSP middleware, template partials, AsyncPaginator |
Enterprise migration path: Django 4.2 LTS -> Django 5.2 LTS (skip 5.0/5.1). Then 5.2 LTS -> 6.0 when ready.
| Feature | 4.2 | 5.2 | 6.0 |
|---|---|---|---|
| Async views | Yes | Yes | Yes |
| Async ORM (aget, acreate) | Yes | Yes | Yes |
| async for on QuerySets | Partial | Full | Full |
| select_related / prefetch_related | Yes | Yes | Yes |
| GeneratedField | No | Yes (5.0) | Yes |
| db_default | No | Yes (5.0) | Yes |
| CompositePrimaryKey | No | Yes | Yes |
| LoginRequiredMiddleware | No | Yes (5.1) | Yes |
| Connection pooling (psycopg3) | No | Yes (5.1) | Yes |
| Background tasks (django.tasks) | No | No | Yes |
| CSP middleware | No | No | Yes |
| Template partials | No | No | Yes |
Load these for deep knowledge on specific topics:
references/architecture.md -- ORM internals (QuerySet evaluation, lazy loading, select_related/prefetch_related), middleware pipeline, template engine, admin internals, auth system, signals, forms, management commands. Load when: architecture questions, ORM behavior, middleware ordering, auth model design.references/best-practices.md -- DRF patterns (serializers, viewsets, permissions), testing (TestCase, pytest-django, factory_boy), deployment (Gunicorn/Uvicorn, WhiteNoise, collectstatic), security (CSRF, HSTS, CSP), performance (N+1 fixes, caching, connection pooling), project structure. Load when: "how should I structure", deployment, security review, performance optimization.references/diagnostics.md -- Common errors (ImproperlyConfigured, FieldError, migration conflicts, circular imports), ORM debugging (django-debug-toolbar, explain(), query logging), performance profiling, async pitfalls. Load when: troubleshooting errors, debugging queries, performance problems.Ready-made manage.py audit (read-only) in scripts/.
scripts/01-deploy-check.sh -- Production deploy check, migration drift, dependency vulnerabilitiestools
kubectl command-line usage and scripting: kubeconfig and context management, output formats (jsonpath, custom-columns, go-template), all major verbs (get, describe, apply, delete, exec, logs, port-forward, rollout, scale, drain), workload resources, config/storage, networking, RBAC, node management, debugging (CrashLoopBackOff, ImagePullBackOff, OOMKilled), and scripting patterns (dry-run, diff, wait, jq, kustomize). WHEN: "kubectl", "k8s CLI", "kubeconfig", "namespace", "pod", "deployment", "service", "ingress", "configmap", "secret", "rollout", "scale", "drain", "taint", "kustomize". Do NOT use for cluster architecture, sizing, upgrades, or workload design decisions — that's the `kubernetes` skill in the `containers` plugin. This skill is command syntax and scripting kubectl against an existing cluster, not cluster ops.
tools
Bash 5.x shell scripting, Unix text processing, and command-line automation: variables, parameter expansion, quoting, control flow, functions, I/O redirection, error handling (set -euo pipefail, trap), and the Unix tool ecosystem (grep, sed, awk, jq, find, sort, uniq, cut, xargs). Covers process management, networking (curl, ssh, rsync, nc), file locking (flock), parallel execution, and production script patterns. WHEN: "Bash", "bash", "shell", "sh", ".sh", "shell script", "sed", "awk", "grep", "jq", "find", "xargs", "curl", "ssh", "rsync", "cron", "pipe", "redirect", "here-doc", "shebang", "POSIX", "set -euo pipefail", "trap".
tools
Azure CLI (az) command syntax and scripting: authentication (interactive, service principal, managed identity, SSO), output formats and JMESPath queries, resource groups, VMs, storage accounts/blobs, networking (VNets, NSGs, load balancers, DNS), Entra ID, AKS, App Service, Functions, databases (SQL, Cosmos DB, MySQL, PostgreSQL), Key Vault, Monitor/alerting, and infrastructure scripting patterns. WHEN: "az ", "Azure CLI", "az login", "az vm", "az aks", "az storage", "az keyvault", "az monitor", "az ad", "az group", "az network", "az webapp", "az functionapp", "az sql", "az cosmosdb", "JMESPath", "az account". Do NOT use for Azure architecture, landing zones, or multi-subscription strategy — that's the `cloud-platforms` plugin. This skill is about command syntax and scripting the CLI, not deciding what to provision.
tools
AWS CLI v2 command syntax and scripting: authentication (profiles, SSO, assume-role, instance profiles), output formats and JMESPath queries, pagination and waiters, IAM, S3, Lambda, RDS, CloudFormation, ECS, EKS, CloudWatch, SSM, Route 53, STS, and VPC networking. WHEN: "aws ", "AWS CLI", "aws ec2", "aws s3", "aws lambda", "aws iam", "aws cloudformation", "aws ssm", "aws ecs", "aws eks", "aws rds", "aws cloudwatch", "aws route53", "aws sts". Do NOT use for AWS architecture, service selection, multi-account strategy, or FinOps — that's the `cloud-platforms` plugin. This skill is about command syntax and scripting the CLI, not deciding what to provision.