Django 5.2 Setup and Configuration
Overview
Django 5.2 setup with modern best practices, based on current AzmX technology stack analysis.
Core Dependencies
From requirements.txt analysis:
Django==5.2
django-ninja==1.3.0 # API framework
jinja2==3.1.6 # Template engine
gunicorn==23.0.0 # WSGI server
dj-database-url==1.2.0 # Database URL parsing
psycopg2-binary==2.9.5 # PostgreSQL adapter
Project Structure
project_name/
├── manage.py
├── requirements.txt
├── .env
├── project_name/
│ ├── __init__.py
│ ├── settings/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── development.py
│ │ ├── staging.py
│ │ └── production.py
│ ├── urls.py
│ └── wsgi.py
├── apps/
│ ├── __init__.py
│ ├── core/
│ ├── users/
│ └── api/
└── static/
Settings Configuration
Base Settings (settings/base.py)
import os
from pathlib import Path
import dj_database_url
BASE_DIR = Path(__file__).resolve().parent.parent.parent
# Security
SECRET_KEY = os.getenv('SECRET_KEY')
DEBUG = False
ALLOWED_HOSTS = []
# Application definition
DJANGO_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
THIRD_PARTY_APPS = [
'ninja',
'storages',
]
LOCAL_APPS = [
'apps.core',
'apps.users',
'apps.api',
]
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
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',
]
ROOT_URLCONF = 'project_name.urls'
# Database - Django 5.1+ Native Pooling
DATABASES = {
'default': dj_database_url.config(
default=os.getenv('DATABASE_URL'),
conn_max_age=0, # Required for pooling
conn_health_checks=True,
)
}
# Enable connection pooling if supported
if 'postgresql' in DATABASES['default']['ENGINE']:
DATABASES['default']['OPTIONS'] = {
'pool': True,
}
# Internationalization
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files configuration
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Default primary key field type
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
Development Settings (settings/development.py)
from .base import *
DEBUG = True
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '0.0.0.0']
# Database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DB_NAME', 'project_dev'),
'USER': os.getenv('DB_USER', 'postgres'),
'PASSWORD': os.getenv('DB_PASSWORD', 'password'),
'HOST': os.getenv('DB_HOST', 'localhost'),
'PORT': os.getenv('DB_PORT', '5433'), # Test environment port
'CONN_MAX_AGE': 0,
'OPTIONS': {
'pool': True,
},
}
}
# Logging
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'INFO',
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'INFO',
'propagate': False,
},
},
}
Production Settings (settings/production.py)
from .base import *
DEBUG = False
ALLOWED_HOSTS = [
'.azmx.sa',
'your-domain.com',
]
# Security settings
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'
# Database with environment-specific port
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DB_NAME'),
'USER': os.getenv('DB_USER'),
'PASSWORD': os.getenv('DB_PASSWORD'),
'HOST': os.getenv('DB_HOST', 'localhost'),
'PORT': os.getenv('DB_PORT', '5434'), # Production port
'CONN_MAX_AGE': 0,
'OPTIONS': {
'pool': True,
},
}
}
# Static files (DigitalOcean Spaces)
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME')
AWS_S3_ENDPOINT_URL = 'https://nyc3.digitaloceanspaces.com'
AWS_DEFAULT_ACL = 'public-read'
Django Ninja API Setup
API Configuration (apps/api/api.py)
from ninja import NinjaAPI
from ninja.security import HttpBearer
from django.http import HttpRequest
api = NinjaAPI(
title="AzmX API",
version="1.0.0",
description="AzmX Application API",
)
class AuthBearer(HttpBearer):
def authenticate(self, request: HttpRequest, token: str):
# Implement your authentication logic
if token == "valid_token":
return token
return None
# Include routers
from .routers import auth, users, core
api.add_router("/auth/", auth.router)
api.add_router("/users/", users.router, auth=AuthBearer())
api.add_router("/core/", core.router, auth=AuthBearer())
URL Configuration (project_name/urls.py)
from django.contrib import admin
from django.urls import path
from apps.api.api import api
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', api.urls),
]
Environment Configuration
Environment Variables (.env)
# Django
SECRET_KEY=your-secret-key-here
DJANGO_SETTINGS_MODULE=project_name.settings.development
DEBUG=True
# Database
DATABASE_URL=postgresql://user:password@localhost:5433/db_name
DB_NAME=project_dev
DB_USER=postgres
DB_PASSWORD=password
DB_HOST=localhost
DB_PORT=5433
# DigitalOcean Spaces
USE_SPACES=FALSE
AWS_ACCESS_KEY_ID=your_spaces_key
AWS_SECRET_ACCESS_KEY=your_spaces_secret
AWS_STORAGE_BUCKET_NAME=your_bucket
# Sentry
SENTRY_DSN=https://[email protected]/project-id
ENVIRONMENT=development
Management Commands
Custom Management Command Example
# apps/core/management/commands/setup_dev_data.py
from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
class Command(BaseCommand):
help = 'Setup development data'
def handle(self, *args, **options):
# Create superuser if it doesn't exist
if not User.objects.filter(is_superuser=True).exists():
User.objects.create_superuser(
username='admin',
email='[email protected]',
password='admin123'
)
self.stdout.write(
self.style.SUCCESS('Successfully created superuser')
)
Deployment Configuration
Gunicorn Configuration (gunicorn.conf.py)
bind = "0.0.0.0:8000"
workers = 3
worker_class = "sync"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 100
timeout = 30
keepalive = 2
preload_app = True
Docker Configuration (Dockerfile)
FROM python:3.11-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy project
COPY . .
# Collect static files
RUN python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "--config", "gunicorn.conf.py", "project_name.wsgi:application"]
Testing Configuration
Test Settings (settings/test.py)
from .base import *
# Use in-memory database for tests
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
}
# Disable migrations for faster tests
class DisableMigrations:
def __contains__(self, item):
return True
def __getitem__(self, item):
return None
MIGRATION_MODULES = DisableMigrations()
# Disable logging during tests
LOGGING_CONFIG = None
Performance Optimizations
Database Query Optimization
# Use select_related and prefetch_related
queryset = Model.objects.select_related('foreign_key').prefetch_related('many_to_many')
# Use database functions
from django.db.models import Count, Avg
Model.objects.aggregate(count=Count('id'), average=Avg('value'))
Caching Configuration
# Cache configuration
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': os.getenv('REDIS_URL', 'redis://127.0.0.1:6379/1'),
}
}
# Session configuration
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'