Python Web Framework

Django

The batteries-included Python web framework for building robust AI agent backends, REST APIs, and admin dashboards. Covers the ORM, views, DRF, middleware, authentication, and production deployment.

1 Core Concepts

MTV Architecture

Django uses the Model-Template-View pattern. Models define data, Views handle logic (similar to controllers), and Templates render HTML. This separation makes AI agent APIs clean and testable.

ORM & Models

Define database tables as Python classes. models.CharField, ForeignKey, JSONField — Django auto-generates migrations. No raw SQL needed. Supports PostgreSQL, MySQL, SQLite.

URL Routing

Map URLs to views via urlpatterns in urls.py. Supports path converters (<int:pk>, <slug:name>), namespaces, and nested includes for modular app routing.

Middleware

Request/response processing hooks that run on every request. Built-in: CSRF protection, session handling, authentication. Write custom middleware for logging, rate limiting, or API key validation for agent endpoints.

2 Setup Guide

🐍 Project Setup

# Create virtual environment
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# Install Django
pip install django djangorestframework

# Create project & app
django-admin startproject agentapi .
python manage.py startapp agents

# Add to INSTALLED_APPS in settings.py
# 'rest_framework', 'agents'

📂 Project Structure

agentapi/
├── manage.py               # CLI entry point
├── agentapi/
│   ├── settings.py         # Configuration (DB, apps, middleware)
│   ├── urls.py             # Root URL routing
│   ├── wsgi.py             # WSGI entrypoint (Gunicorn)
│   └── asgi.py             # ASGI entrypoint (Uvicorn/Daphne)
└── agents/
    ├── models.py           # Database models
    ├── views.py            # Request handlers / API views
    ├── serializers.py      # DRF serializers
    ├── urls.py             # App-level URL routing
    ├── admin.py            # Admin panel config
    └── tests.py            # Unit tests

3 Key Patterns & Usage

Models & Migrations

# agents/models.py
from django.db import models

class Agent(models.Model):
    name = models.CharField(max_length=200)
    model_provider = models.CharField(max_length=50)
    system_prompt = models.TextField()
    config = models.JSONField(default=dict)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['model_provider', 'is_active'])
        ]

# Run migrations
# python manage.py makemigrations
# python manage.py migrate

DRF Serializers & ViewSets

# agents/serializers.py
from rest_framework import serializers
from .models import Agent

class AgentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Agent
        fields = '__all__'

# agents/views.py
from rest_framework import viewsets
from .models import Agent
from .serializers import AgentSerializer

class AgentViewSet(viewsets.ModelViewSet):
    queryset = Agent.objects.all()
    serializer_class = AgentSerializer
    filterset_fields = ['model_provider', 'is_active']

# agents/urls.py
from rest_framework.routers import DefaultRouter
from .views import AgentViewSet

router = DefaultRouter()
router.register('agents', AgentViewSet)
urlpatterns = router.urls

Authentication & Permissions

# settings.py — DRF auth defaults
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/hour',
        'user': '1000/hour',
    }
}

Custom Middleware

# agentapi/middleware.py
import time, logging

logger = logging.getLogger(__name__)

class RequestTimingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        start = time.perf_counter()
        response = self.get_response(request)
        duration = time.perf_counter() - start
        logger.info(
            f"{request.method} {request.path} "
            f"→ {response.status_code} [{duration:.3f}s]"
        )
        response['X-Request-Duration'] = f"{duration:.3f}"
        return response

# Add to MIDDLEWARE list in settings.py
# 'agentapi.middleware.RequestTimingMiddleware'

4 Admin Panel & Testing

Django Admin

Auto-generated admin UI at /admin/. Register models with admin.site.register(Agent, AgentAdmin). Customize with list_display, search_fields, list_filter, and inline editing. Production-ready CRUD without writing a single line of frontend.

Unit Testing

Use TestCase for DB-backed tests and APITestCase for DRF endpoints. Django creates a test database, runs migrations, and rolls back after each test. python manage.py test discovers all tests.py files automatically.

Signals & Hooks

React to model events with post_save, pre_delete signals. Trigger agent provisioning when a new Agent record is created. Use @receiver decorator for clean signal handling. Decouple business logic from view code.

5 Production Deployment

Gunicorn + Nginx

# Install Gunicorn
pip install gunicorn

# Run with workers (2 × CPU cores + 1)
gunicorn agentapi.wsgi:application \
    --workers 9 \
    --bind 0.0.0.0:8000 \
    --timeout 120 \
    --access-logfile - \
    --error-logfile -

# Nginx reverse proxy config
server {
    listen 80;
    server_name api.yourdomain.com;

    location /static/ {
        alias /app/staticfiles/;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Docker Deployment

# Dockerfile
FROM python:3.12-slim
WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
RUN python manage.py collectstatic --noinput

EXPOSE 8000
CMD ["gunicorn", "agentapi.wsgi:application", \
     "--workers", "4", "--bind", "0.0.0.0:8000"]

# settings.py production checklist
DEBUG = False
ALLOWED_HOSTS = ['api.yourdomain.com']
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
DATABASES = {
    'default': dj_database_url.config()
}

6 Scaling & Production

Gunicorn Workers

Scale horizontally by increasing Gunicorn workers (formula: 2 × CPU + 1). For async workloads use --worker-class uvicorn.workers.UvicornWorker. Behind a load balancer, run multiple containers with 4 workers each.

Database Connection Pooling

Django creates a new DB connection per request by default. Use django-db-connection-pool or PgBouncer to pool connections. Set CONN_MAX_AGE=600 to persist connections across requests. Critical at 1000+ req/sec.

Caching (Redis)

Use django-redis as the cache backend. Cache query results with @cache_page(60*15) decorator. For DRF, cache serializer responses. Reduces DB queries by 80-90% on read-heavy agent status endpoints.

Read Replicas

Configure multiple databases in DATABASES and use a database router to direct reads to replicas. Use .using('replica') for explicit queries. Scales read throughput linearly with replica count.

Async Views (Django 5+)

Define views with async def for I/O-bound agent tasks (LLM API calls, external HTTP). Run under Uvicorn or Daphne ASGI servers. Handles 10× more concurrent connections than sync Gunicorn for agent orchestration endpoints.

Static & Media Files

Run collectstatic at build time. Serve static files from Nginx, WhiteNoise, or a CDN — never from Gunicorn. Upload user/agent files to S3 via django-storages. Offloads file serving from the application layer entirely.

7 FAQ

Django vs. FastAPI — which should I choose?
Django excels when you need an ORM, admin panel, auth system, and full-stack features out of the box. FastAPI is better for pure API microservices with async-first design and automatic OpenAPI docs. For AI agent backends that need a database, admin UI, and background tasks — Django + DRF + Celery is the proven stack.
How do I handle long-running LLM calls in Django?
Never block a Gunicorn worker on a 30-second LLM API call. Use Celery to offload the task to a background worker. Return a 202 Accepted with a task ID, and let the client poll or use WebSocket for the result. Alternatively, use Django's async views with Uvicorn for non-blocking I/O.
What database should I use with Django?
PostgreSQL is the gold standard for Django. It supports JSONField, full-text search, array fields, and range types natively. Django's ORM has first-class PostgreSQL support including django.contrib.postgres extensions. SQLite is fine for development only.
How do I deploy Django with HTTPS?
Use Nginx as a reverse proxy with Let's Encrypt SSL certificates (certbot). Set SECURE_SSL_REDIRECT = True, SESSION_COOKIE_SECURE = True, and CSRF_COOKIE_SECURE = True in settings. Nginx terminates TLS and proxies to Gunicorn over HTTP on localhost. Or use a cloud load balancer (ALB, Cloud Load Balancing) for managed TLS.
How do I structure a large Django project?
Split into multiple Django apps by domain: agents/, tasks/, users/, billing/. Each app has its own models, views, serializers, and tests. Use a core/ app for shared utilities. Keep business logic in service layers (not in views or serializers) for testability.