Chapter 47 · Instrument Integration
Subchapter 47.14
references/EXAMPLE-django.mdMarkdown30 KBView on GitHub
Repository: https://github.com/PostHog/context-mill Path: basics/django
This is a Django (opens in a new tab) example demonstrating PostHog integration with product analytics, error tracking, feature flags, and user identification.
pip install posthogCreate a .env file in the root directory:
POSTHOG_PROJECT_TOKEN=your_posthog_project_token
POSTHOG_HOST=https://us.i.posthog.comGet your PostHog project token from your PostHog project settings (opens in a new tab).
python manage.py migratepython manage.py runserverOpen http://localhost:8000 (opens in a new tab) with your browser to see the app.
django/
├── manage.py # Django management script
├── requirements.txt # Python dependencies
├── .env.example # Environment variable template
├── .gitignore
├── posthog_example/
│ ├── __init__.py
│ ├── settings.py # Django settings with PostHog config
│ ├── urls.py # URL routing
│ ├── wsgi.py # WSGI application
│ └── asgi.py # ASGI application
└── core/
├── __init__.py
├── apps.py # AppConfig with PostHog initialization
├── views.py # Views with event tracking examples
├── urls.py # App URL patterns
└── templates/
└── core/
├── base.html # Base template
├── home.html # Home/login page
├── burrito.html # Burrito page with event tracking
├── dashboard.html # Dashboard with feature flag example
└── profile.html # Profile pageimport posthog
from django.conf import settings
class CoreConfig(AppConfig):
name = 'core'
def ready(self):
posthog.api_key = settings.POSTHOG_PROJECT_TOKEN
posthog.host = settings.POSTHOG_HOSTimport os
# PostHog configuration
POSTHOG_PROJECT_TOKEN = os.environ.get('POSTHOG_PROJECT_TOKEN', '<ph_project_token>')
POSTHOG_HOST = os.environ.get('POSTHOG_HOST', 'https://us.i.posthog.com')
MIDDLEWARE = [
# ... other middleware
'posthog.integrations.django.PosthogContextMiddleware',
]The PostHog SDK includes a Django middleware that automatically wraps all requests with a context. It extracts session and user information from request headers and tags all events captured during the request.
The middleware automatically extracts:
X-POSTHOG-SESSION-ID headerX-POSTHOG-DISTINCT-ID header$current_url$request_methodimport posthog
def login_view(request):
# ... authentication logic
if user:
with posthog.new_context():
posthog.identify_context(str(user.id))
posthog.tag('email', user.email)
posthog.tag('username', user.username)
posthog.capture('user_logged_in', properties={
'login_method': 'email',
})import posthog
def consider_burrito(request):
user_id = str(request.user.id) if request.user.is_authenticated else 'anonymous'
with posthog.new_context():
posthog.identify_context(user_id)
posthog.capture('burrito_considered', properties={
'total_considerations': request.session.get('burrito_count', 0),
})import posthog
def dashboard_view(request):
user_id = str(request.user.id) if request.user.is_authenticated else 'anonymous'
show_new_feature = posthog.feature_enabled(
'new-dashboard-feature',
distinct_id=user_id
)
return render(request, 'core/dashboard.html', {
'show_new_feature': show_new_feature
})Capture exceptions manually using capture_exception():
import posthog
def profile_view(request):
try:
risky_operation()
except Exception as e:
posthog.capture_exception(e)If you’re using PostHog’s JavaScript SDK on the frontend, enable tracing headers to connect frontend sessions with backend events:
posthog.init('<ph_project_token>', {
api_host: 'https://us.i.posthog.com',
__add_tracing_headers: ['your-backend-domain.com'],
})This automatically adds X-POSTHOG-SESSION-ID and X-POSTHOG-DISTINCT-ID headers to requests, which the Django middleware extracts to maintain context.
POSTHOG_PROJECT_TOKEN=
POSTHOG_HOST=https://us.i.posthog.com
DJANGO_SECRET_KEY=your-secret-key-here
DEBUG=True
# Core app for PostHog Django example
"""
Django AppConfig that initializes PostHog when the application starts.
This ensures the SDK is configured once when Django starts, making it available throughout the application.
"""
from django.apps import AppConfig
from django.conf import settings
class CoreConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'core'
def ready(self):
"""
Initialize PostHog when Django starts.
This method is called once when Django starts. We configure the
PostHog SDK here so it's available everywhere in the application.
Note: Import posthog inside this method to avoid import issues
during Django's startup sequence.
"""
import posthog
# Configure PostHog with settings from Django settings
posthog.api_key = settings.POSTHOG_PROJECT_TOKEN
posthog.host = settings.POSTHOG_HOST
# Disable PostHog if configured (useful for testing)
if settings.POSTHOG_DISABLED:
posthog.disabled = True
# Optional: Enable debug mode in development
if settings.DEBUG:
posthog.debug = True
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}PostHog Django example{% endblock %}</title>
<style>
* {
box-sizing
{% extends 'core/base.html' %}
{% block title %}Burrito - PostHog Django example{% endblock %}
{% block content %}
<div class="card">
<h1>Burrito consideration tracker</h1>
<p>This page demonstrates custom event tracking with PostHog.</p>
</div>
<div class="card" style="text-align: center;">
<h2>Times considered</h2>
<div class="count" id="burrito-count">{{ burrito_count }}</div>
<button onclick="considerBurrito()" style="font-size: 18px; padding: 15px 30px;">
Consider a burrito
</button>
</div>
<div class="card">
<h3>How event tracking works</h3>
<p>Each time you click the button, a <code>burrito_considered</code> event is sent to PostHog:</p>
<pre style="background: #f3f4f6; padding: 15px; border-radius: 5px; overflow-x: auto; margin-top: 15px;"><code>from posthog import new_context, identify_context, capture
with new_context():
identify_context(user_id)
capture('burrito_considered', properties={
'total_considerations': count,
})</code></pre>
</div>
{% endblock %}
{% block scripts %}
<script>
async function considerBurrito() {
try {
const response = await fetch('{% url "consider_burrito" %}', {
method: 'POST',
headers: {
'X-CSRFToken': '{{ csrf_token }}',
'Content-Type': 'application/json',
},
});
const data = await response.json();
if (data.success) {
document.getElementById('burrito-count').textContent = data.count;
}
} catch (error) {
console.error('Error:', error);
}
}
</script>
{% endblock %}
{% extends 'core/base.html' %}
{% block title %}Dashboard - PostHog Django example{% endblock %}
{% block content %}
<div class="card">
<h1>Dashboard</h1>
<p>Welcome back, <strong>{{ user.username }}</strong>!</p>
</div>
<div class="card">
<h2>Feature flags</h2>
<p>Feature flags allow you to control feature rollouts and run A/B tests.</p>
{% if show_new_feature %}
<div class="feature-flag">
<h3>New feature enabled!</h3>
<p>
This section is only visible because the <code>new-dashboard-feature</code>
flag is enabled for your user.
</p>
{% if feature_config %}
<p><strong>Feature config:</strong> {{ feature_config }}</p>
{% endif %}
</div>
{% else %}
<div style="background: #f3f4f6; padding: 15px; border-radius: 8px; margin-top: 15px;">
<p>
The <code>new-dashboard-feature</code> flag is not enabled for your user.
Create this flag in your PostHog project to see it in action.
</p>
</div>
{% endif %}
</div>
<div class="card">
<h3>How feature flags work</h3>
<pre style="background: #f3f4f6; padding: 15px; border-radius: 5px; overflow-x: auto;"><code># Check if a feature flag is enabled
show_feature = posthog.feature_enabled(
'new-dashboard-feature',
distinct_id=user_id,
person_properties={
'email': user.email,
'is_staff': user.is_staff,
}
)
# Get feature flag payload for configuration
config = posthog.get_feature_flag_payload(
'new-dashboard-feature',
distinct_id=user_id,
)</code></pre>
</div>
{% endblock %}
{% extends 'core/base.html' %}
{% block title %}Login - PostHog Django example{% endblock %}
{% block content %}
<div class="card">
<h1>PostHog Django example</h1>
<p>Welcome! This example demonstrates PostHog integration with Django.</p>
</div>
<div class="card">
<h2>Login</h2>
<p>Login to see PostHog analytics in action.</p>
<form method="post" style="margin-top: 20px;">
{% csrf_token %}
<input type="text" name="username" placeholder="Username" required>
<input type="password" name="password" placeholder="Password" required>
<button type="submit">Login</button>
</form>
<p style="margin-top: 15px; color: #666; font-size: 14px;">
Tip: Create a user with <code>python manage.py createsuperuser</code>
</p>
</div>
<div class="card">
<h3>What this example demonstrates</h3>
<ul style="padding-left: 20px;">
<li><strong>User identification</strong> - Users are identified with <code>identify_context()</code> on login</li>
<li><strong>Pageview tracking</strong> - Middleware extracts session and user context</li>
<li><strong>Event tracking</strong> - Custom events captured with <code>capture()</code> in context</li>
<li><strong>Feature flags</strong> - Conditional features with <code>posthog.feature_enabled()</code></li>
<li><strong>Error tracking</strong> - Exceptions captured with <code>capture_exception()</code></li>
</ul>
</div>
{% endblock %}
{% extends 'core/base.html' %}
{% block title %}Profile - PostHog Django example{% endblock %}
{% block content %}
<div class="card">
<h1>Profile</h1>
<p>This page demonstrates error tracking with PostHog.</p>
</div>
<div class="card">
<h2>User information</h2>
<table style
"""
URL configuration for the core app.
This module defines all the URL patterns for the PostHog example views.
"""
from django.urls import path
from . import views
urlpatterns = [
# Home login page
path('', views.home_view, name='home'),
# Authentication
path('logout/', views.logout_view, name='logout'),
# Dashboard with feature flags
path('dashboard/', views.dashboard_view, name='dashboard'),
# Burrito example for event tracking
path('burrito/', views.burrito_view, name='burrito'),
path('api/burrito/consider/', views.consider_burrito_view, name='consider_burrito'),
# Profile with error tracking
path('profile/', views.profile_view, name='profile'),
path('api/trigger-error/', views.trigger_error_view, name='trigger_error'),
# Group analytics example
path('api/group-analytics/', views.group_analytics_view, name='group_analytics'),
]
"""Django views demonstrating PostHog integration patterns"""
import posthog
from posthog import new_context, identify_context, tag, capture
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.http import JsonResponse
from django.views.decorators.http import require_POST
def home_view(request):
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'posthog_example.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
# PostHog Django example project
"""
ASGI config for PostHog example project
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'posthog_example.settings')
application = get_asgi_application()
"""Django settings for PostHog example project"""
import os
from pathlib import Path
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
pass
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'django-insecure-example-key-change-in-production')
DEBUG = os.environ.get('DEBUG', 'True').lower() == 'true'
ALLOWED_HOSTS = ['localhost', '127.0.0.1']
# PostHog configuration
POSTHOG_PROJECT_TOKEN = os.environ.get('POSTHOG_PROJECT_TOKEN', '<ph_project_token>')
POSTHOG_HOST = os.environ.get('POSTHOG_HOST', 'https://us.i.posthog.com')
POSTHOG_DISABLED = os.environ.get('POSTHOG_DISABLED', 'False').lower() == 'true'
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'core.apps.CoreConfig',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'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',
'posthog.integrations.django.PosthogContextMiddleware',
]
ROOT_URLCONF = 'posthog_example.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'posthog_example.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
"""
URL configuration for PostHog example project
"""
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
# Include the core app URLs for PostHog examples
path('', include('core.urls')),
]
"""
WSGI config for PostHog example project
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'posthog_example.settings')
application = get_wsgi_application()
Django>=4.2,<5.0
posthog # Always use latest version
python-dotenv>=1.0.0
Nearby