Documentation menu: Django
Django integration
Add theBizness.ai analytics to your Django application with template integration and robust configuration.
Setup time
5 minutes
Difficulty
Medium
Performance
Pythonic
Choose your implementation method
Base template method
Step 1: Environment variables
Add to your .env file:
# .env
ANALYTICS_ENABLED=True
ANALYTICS_WEBSITE_ID=your-website-id
ANALYTICS_DOMAIN=yourdomain.comStep 2: Update the settings
# settings.py
import os
from decouple import config # pip install python-decouple
# Analytics Configuration
ANALYTICS_ENABLED = config('ANALYTICS_ENABLED', default=False, cast=bool)
ANALYTICS_WEBSITE_ID = config('ANALYTICS_WEBSITE_ID', default='')
ANALYTICS_DOMAIN = config('ANALYTICS_DOMAIN', default='')
ANALYTICS_SCRIPT_URL = config('ANALYTICS_SCRIPT_URL', default='https://thebizness.ai/js/script.js')
# Context processors (add to TEMPLATES settings)
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'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',
'myapp.context_processors.analytics', # Add this line
],
},
},
]Step 3: Create the context processor
# myapp/context_processors.py
from django.conf import settings
def analytics(request):
"""
Add analytics configuration to template context
"""
return {
'ANALYTICS_ENABLED': getattr(settings, 'ANALYTICS_ENABLED', False),
'ANALYTICS_WEBSITE_ID': getattr(settings, 'ANALYTICS_WEBSITE_ID', ''),
'ANALYTICS_DOMAIN': getattr(settings, 'ANALYTICS_DOMAIN', ''),
'ANALYTICS_SCRIPT_URL': getattr(settings, 'ANALYTICS_SCRIPT_URL', 'https://thebizness.ai/js/script.js'),
}Step 4: Update the base template
<!-- templates/base.html -->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}My Django App{% endblock %}</title>
{% block extra_css %}{% endblock %}
<!-- theBizness Analytics -->
{% if ANALYTICS_ENABLED and ANALYTICS_WEBSITE_ID %}
<script
async
defer
data-website-id="{{ ANALYTICS_WEBSITE_ID }}"
data-domain="{{ ANALYTICS_DOMAIN|default:request.get_host }}"
src="https://thebizness.ai/js/script.js">
</script>
{% endif %}
</head>
<body>
{% block content %}{% endblock %}
{% block extra_js %}{% endblock %}
</body>
</html>- • Environment-based configuration
- • Context processor for template availability
- • Auto-detection of domain from request
- • Easy to disable for development
Middleware implementation
Create the analytics middleware
# myapp/middleware.py
from django.conf import settings
from django.utils.html import escape
from django.utils.safestring import mark_safe
class AnalyticsMiddleware:
"""
Middleware to inject analytics script into HTML responses
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
# Leave excluded paths untouched (the admin site by default, set ANALYTICS_EXCLUDE_PATHS to change it)
excluded = getattr(settings, 'ANALYTICS_EXCLUDE_PATHS', ('/admin/',))
if request.path.startswith(tuple(excluded)):
return response
# Only inject on HTML responses. Streamed or encoded bodies (for example gzipped by GZipMiddleware) are left alone
if (
getattr(settings, 'ANALYTICS_ENABLED', False) and
response.get('Content-Type', '').startswith('text/html') and
response.status_code == 200 and
not response.streaming and
not response.has_header('Content-Encoding')
):
analytics_script = self._get_analytics_script(request)
if analytics_script:
charset = response.charset
content = response.content.decode(charset)
content = content.replace('</head>', f'{analytics_script}</head>')
response.content = content.encode(charset)
response['Content-Length'] = len(response.content)
return response
def _get_analytics_script(self, request):
website_id = getattr(settings, 'ANALYTICS_WEBSITE_ID', '')
domain = getattr(settings, 'ANALYTICS_DOMAIN', '') or request.get_host()
script_url = getattr(settings, 'ANALYTICS_SCRIPT_URL', 'https://thebizness.ai/js/script.js')
if not website_id:
return ''
return f'''
<script async defer data-website-id="{escape(website_id)}" data-domain="{escape(domain)}" src="{escape(script_url)}"></script>'''Register the middleware
Add to settings.py MIDDLEWARE list:
MIDDLEWARE = [
# ... other middleware
'myapp.middleware.AnalyticsMiddleware',
]Place it below GZipMiddleware and any other middleware that changes the response body: Django passes responses up the list from the bottom, so a class placed above GZipMiddleware sees the compressed body and adds no script.
Custom template tag
Create the template tags
# myapp/templatetags/analytics_tags.py
from django import template
from django.conf import settings
from django.utils.html import escape
from django.utils.safestring import mark_safe
register = template.Library()
@register.simple_tag(takes_context=True)
def analytics_script(context):
"""
Template tag to render analytics script
Usage: {% load analytics_tags %} {% analytics_script %}
"""
request = context.get('request')
if not getattr(settings, 'ANALYTICS_ENABLED', False):
return ''
website_id = getattr(settings, 'ANALYTICS_WEBSITE_ID', '')
if not website_id:
return ''
domain = getattr(settings, 'ANALYTICS_DOMAIN', '')
if not domain and request:
domain = request.get_host()
script_url = getattr(settings, 'ANALYTICS_SCRIPT_URL', 'https://thebizness.ai/js/script.js')
script = f'''<script async defer data-website-id="{escape(website_id)}" data-domain="{escape(domain)}" src="{escape(script_url)}"></script>'''
return mark_safe(script)
@register.filter
def is_analytics_enabled(value):
"""
Template filter to check if analytics is enabled
Usage: {% if request|is_analytics_enabled %}
"""
return getattr(settings, 'ANALYTICS_ENABLED', False)Usage in templates
<!-- Load the template tags -->
{% load analytics_tags %}
<!-- Use in your template head -->
{% analytics_script %}
<!-- Conditional usage -->
{% if request|is_analytics_enabled %}
<p>Analytics is enabled</p>
{% endif %}Management command and app config
Management command
# management/commands/setup_analytics.py
from django.core.management.base import BaseCommand
from django.conf import settings
import os
class Command(BaseCommand):
help = 'Setup theBizness analytics configuration'
def add_arguments(self, parser):
parser.add_argument('website_id', type=str, help='Your theBizness website ID')
parser.add_argument('--domain', type=str, help='Your domain (optional)')
parser.add_argument('--env-file', type=str, default='.env', help='Environment file path')
def handle(self, *args, **options):
website_id = options['website_id']
domain = options.get('domain', '')
env_file = options['env_file']
# Update or create .env file
env_vars = {
'ANALYTICS_ENABLED': 'True',
'ANALYTICS_WEBSITE_ID': website_id,
}
if domain:
env_vars['ANALYTICS_DOMAIN'] = domain
self._update_env_file(env_file, env_vars)
self.stdout.write(
self.style.SUCCESS(
f'Analytics configuration updated successfully!\n'
f'Website ID: {website_id}\n'
f'Domain: {domain or "Auto-detected"}'
)
)
if not domain:
self.stdout.write(
self.style.WARNING(
'No domain specified. Domain will be auto-detected from request.'
)
)
def _update_env_file(self, env_file, variables):
"""Update or create environment file with new variables"""
env_path = os.path.join(settings.BASE_DIR, env_file)
# Read existing lines so comments, blank lines and their order are kept
lines = []
if os.path.exists(env_path):
with open(env_path, 'r') as f:
lines = f.read().splitlines()
# Replace the value of each existing variable in place, and leave every other line as it was
pending = dict(variables)
for i, line in enumerate(lines):
key = line.split('=', 1)[0].strip()
if '=' in line and key in variables:
lines[i] = f'{key}={variables[key]}'
pending.pop(key, None)
# Append the variables the file did not have yet
lines.extend(f'{key}={value}' for key, value in pending.items())
# Write back to file
with open(env_path, 'w') as f:
f.write('\n'.join(lines) + '\n')App configuration
# myapp/apps.py
from django.apps import AppConfig
from django.conf import settings
class MyAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myapp'
def ready(self):
# Validate analytics configuration on startup
self._validate_analytics_config()
def _validate_analytics_config(self):
if getattr(settings, 'ANALYTICS_ENABLED', False):
website_id = getattr(settings, 'ANALYTICS_WEBSITE_ID', '')
if not website_id:
import warnings
warnings.warn(
'Analytics is enabled but ANALYTICS_WEBSITE_ID is not set. '
'Analytics tracking will not work.',
RuntimeWarning
)Usage
# Setup analytics with management command
python manage.py setup_analytics your-website-id --domain yourdomain.com
# Install python-decouple for environment variables
pip install python-decoupleVerify installation
Development check
- 1. Run
python manage.py runserverto check that the page loads - 2. Visit your Django app
- 3. View page source to check script
- 4. Check browser DevTools Network tab for script.js. The console shows a "Tracking disabled on localhost" warning here, which is expected
Django specific
- 1. Test with different views/URLs
- 2. Check admin interface (should NOT have script)
- 3. Verify environment variables
- 4. Test with DEBUG=False
- • The tracker does nothing on localhost, *.local, *.test, private IP addresses or file:// pages. It logs a "Tracking disabled on localhost" warning in the console. Verify on a publicly reachable domain.
- • Use different settings for development/production
- • Consider disabling analytics in DEBUG mode
- • Test with both authenticated and anonymous users
- • Make sure ALLOWED_HOSTS includes your domain
- • If your site sends a Content-Security-Policy header, allow https://thebizness.ai in script-src and connect-src. Blocked requests show only as CSP errors in the browser console
Django integration complete
Your Django application is now tracking analytics with Python elegance. Here's what to explore next:
Revenue attribution
Connect Stripe or send a purchase event from your order-complete page
Custom events
Send events from the browser with window.thebizness(...)
Server-side reporting
Report sales from a view with the requests library and your ingest secret as a Bearer token. Keep the secret in an environment variable