DjangoConfig Base Class
The DjangoConfig base class is the foundation of Django-CFG’s type-safe configuration system.
Overview
DjangoConfig extends Pydantic’s BaseModel to provide:
- âś… Type-safe configuration with Pydantic v2 validation
- âś… Environment detection (development, staging, production)
- âś… Smart defaults based on environment
- âś… Django settings generation from configuration models
- âś… YAML configuration support
- âś… Path resolution for project structure
Complete Class Definition
from pydantic import BaseModel, Field, ConfigDict, field_validator
from typing import Dict, List, Any, Optional, Union
from pathlib import Path
class DjangoConfig(BaseModel):
"""
Base configuration class for Django projects.
Handles all Django settings generation and validation.
"""
model_config = ConfigDict(
validate_assignment=True,
extra="allow",
env_prefix="DJANGO_",
populate_by_name=True,
validate_default=True,
)
# === Core Django Settings ===
secret_key: str = Field(
...,
description="Django SECRET_KEY",
min_length=50
)
debug: bool = Field(
default=False,
description="Django DEBUG setting"
)
debug_warnings: bool = Field(
default=False,
description="Enable detailed warnings traceback (shows full stack trace for RuntimeWarnings)"
)
# === Timezone Configuration ===
admin_timezone: Optional[str] = Field(
default=None,
description=(
"Timezone for admin display (e.g., 'Asia/Seoul', 'Europe/Moscow'). "
"If None, uses system timezone via tzlocal."
)
)
# Note: ALLOWED_HOSTS is auto-generated from security_domains
# See get_allowed_hosts() method and security_domains field
# === Project Structure ===
project_name: str = Field(
...,
description="Project name for identification"
)
project_apps: List[str] = Field(
default_factory=list,
description="List of project-specific Django apps"
)
# === URL Configuration ===
root_urlconf: str = Field(
default="",
description="Django ROOT_URLCONF setting"
)
wsgi_application: str = Field(
default="",
description="Django WSGI_APPLICATION setting"
)
# === Custom User Model ===
auth_user_model: Optional[str] = Field(
default=None,
description="Custom user model (AUTH_USER_MODEL)"
)
# === Database Configuration ===
databases: Dict[str, "DatabaseConfig"] = Field(
default_factory=dict,
description="Database connections"
)
database_routing: List["DatabaseRoutingRule"] = Field(
default_factory=list,
description="Database routing rules"
)
# === Cache Configuration ===
cache_default: Optional["CacheConfig"] = Field(
default=None,
description="Default cache backend"
)
cache_sessions: Optional["CacheConfig"] = Field(
default=None,
description="Sessions cache backend"
)
# === Security Configuration ===
security_domains: List[str] = Field(
default_factory=list,
description="Domains for automatic security configuration (optional in development)"
)
ssl_redirect: Optional[bool] = Field(
default=None,
description="Force SSL redirect (None = disabled, assumes reverse proxy handles SSL)"
)
cors_allow_headers: List[str] = Field(
default_factory=list,
description="CORS allowed headers"
)
# === Services Configuration ===
email: Optional["EmailConfig"] = Field(
default=None,
description="Email service configuration"
)
telegram: Optional["TelegramConfig"] = Field(
default=None,
description="Telegram service configuration"
)
# === Third-Party Integrations ===
openapi_client: Optional[OpenAPIClientConfig] = Field(
default=None,
description="Django-CFG API Client Generation API zones configuration"
)
unfold: Optional["UnfoldConfig"] = Field(
default=None,
description="Unfold dashboard configuration"
)
dashboard: Optional["DashboardConfig"] = Field(
default=None,
description="Dashboard configuration with callbacks"
)
# === Built-in Apps Configuration ===
enable_accounts: bool = Field(
default=False,
description="Enable django_cfg.apps.accounts"
)
enable_agents: bool = Field(
default=False,
description="Enable django_cfg.apps.agents"
)
enable_knowbase: bool = Field(
default=False,
description="Enable django_cfg.apps.knowbase"
)
enable_leads: bool = Field(
default=False,
description="Enable django_cfg.apps.leads"
)
enable_maintenance: bool = Field(
default=False,
description="Enable django_cfg.apps.maintenance"
)
enable_newsletter: bool = Field(
default=False,
description="Enable django_cfg.apps.newsletter"
)
enable_support: bool = Field(
default=False,
description="Enable django_cfg.apps.support"
)
# === Environment Configuration ===
environments: Optional["EnvironmentConfig"] = Field(
default=None,
description="Environment-specific configuration files"
)
# === Middleware Configuration ===
custom_middleware: List[str] = Field(
default_factory=list,
description="Custom middleware classes"
)
# === Logging Configuration ===
logging: Optional["LoggingConfig"] = Field(
default=None,
description="Logging configuration"
)
# === Internal State ===
_environment: Optional[str] = Field(
default=None,
description="Detected environment",
exclude=True
)
_base_dir: Optional[Path] = Field(
default=None,
description="Project base directory",
exclude=True
)
_django_settings: Optional[Dict[str, Any]] = Field(
default=None,
description="Generated Django settings cache",
exclude=True
)Initialization Methods
init
def __init__(self, **data):
"""Initialize configuration with environment detection"""
super().__init__(**data)
self._detect_environment()
self._resolve_paths()
self._apply_smart_defaults()
self._load_environment_config()Initialization flow:
- Environment Detection - Detects current environment (dev/staging/prod)
- Path Resolution - Finds project root, detects URLs/WSGI
- Smart Defaults - Applies environment-aware defaults
- YAML Loading - Loads environment-specific YAML configuration
_detect_environment
def _detect_environment(self) -> None:
"""Detect current environment"""
from django_cfg.core.environment import EnvironmentDetector
self._environment = EnvironmentDetector.detect_environment()Detects environment based on:
- Environment variables (
DJANGO_ENV,ENV) - Hostname patterns
- File existence (
.env.production,.env.development)
_resolve_paths
def _resolve_paths(self) -> None:
"""Resolve project paths"""
from django_cfg.utils.path_resolution import PathResolver
self._base_dir = PathResolver.find_project_root()
# Auto-detect URL configuration if not set
if not self.root_urlconf:
self.root_urlconf = PathResolver.detect_root_urlconf()
if not self.wsgi_application:
self.wsgi_application = PathResolver.detect_wsgi_application()Automatically detects:
- BASE_DIR - Project root directory
- ROOT_URLCONF - URL configuration module
- WSGI_APPLICATION - WSGI application path
_apply_smart_defaults
def _apply_smart_defaults(self) -> None:
"""Apply environment-aware defaults"""
from django_cfg.utils.smart_defaults import SmartDefaults
# Apply cache defaults
if self.cache_default:
self.cache_default = SmartDefaults.configure_cache_backend(
self.cache_default, self._environment, self.debug
)
if self.cache_sessions:
self.cache_sessions = SmartDefaults.configure_cache_backend(
self.cache_sessions, self._environment, self.debug
)Smart defaults based on environment:
Development:
- Cache: LocMem (in-memory)
- Debug: True
- SSL: Disabled
Production:
- Cache: Redis (if configured)
- Debug: False
- SSL: Enabled
Settings Generation Methods
get_all_settings
def get_all_settings(self) -> Dict[str, Any]:
"""Generate complete Django settings dictionary"""
if self._django_settings is None:
from django_cfg.core.generation import SettingsGenerator
self._django_settings = SettingsGenerator.generate(self)
return self._django_settingsExample usage:
# config.py
config = MyConfig(secret_key="...", debug=True)
# settings.py
from config import config
globals().update(config.get_all_settings())get_installed_apps
def get_installed_apps(self) -> List[str]:
"""Get complete list of installed apps"""
apps = [
# Django core apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
# Add third-party apps based on configuration
if self.openapi_client:
apps.append('django_cfg.client')
if self.unfold:
apps.append('unfold')
# Add project apps
apps.extend(self.project_apps)
return appsget_middleware
def get_middleware(self) -> List[str]:
"""Get complete middleware stack"""
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',
]
# Add CORS middleware if domains are configured
if self.security_domains:
middleware.insert(1, 'corsheaders.middleware.CorsMiddleware')
# Add custom middleware
middleware.extend(self.custom_middleware)
return middlewareget_allowed_hosts
def get_allowed_hosts(self) -> List[str]:
"""
Get ALLOWED_HOSTS auto-generated from security_domains.
Automatically includes:
- All domains from security_domains
- www subdomain for each domain
- localhost, 127.0.0.1, 0.0.0.0 for development
Returns:
List of allowed host names
Example:
>>> config = DjangoConfig(security_domains=["myapp.com"])
>>> config.get_allowed_hosts()
['myapp.com', 'www.myapp.com', 'localhost', '127.0.0.1', '0.0.0.0']
"""
allowed_hosts = []
# Add domains from security_domains
for domain in self.security_domains:
# Remove protocol if present
if "://" in domain:
domain = domain.split("://")[1]
# Remove path if present
if "/" in domain:
domain = domain.split("/")[0]
# Add domain
allowed_hosts.append(domain)
# Add www subdomain
if not domain.startswith("www."):
allowed_hosts.append(f"www.{domain}")
# Always include localhost for development and health checks
for local_host in ["localhost", "127.0.0.1", "0.0.0.0"]:
if local_host not in allowed_hosts:
allowed_hosts.append(local_host)
return allowed_hostsValidation
validate_configuration
def validate_configuration(self) -> List[str]:
"""Validate configuration and return list of errors"""
from django_cfg.core.validation import ConfigurationValidator
return ConfigurationValidator.validate(self)Example usage:
config = MyConfig(secret_key="short")
errors = config.validate_configuration()
if errors:
for error in errors:
print(f"Error: {error}")Usage Examples
Basic Configuration
# config.py
from django_cfg import DjangoConfig
from pydantic import BaseModel
class DatabaseConfig(BaseModel):
url: str = "postgresql://localhost/mydb"
class MyConfig(DjangoConfig):
secret_key: str = "your-secret-key-here-minimum-50-characters-long-string"
debug: bool = True
project_name: str = "My Project"
project_apps: list = ["apps.blog", "apps.shop"]
database: DatabaseConfig = DatabaseConfig()With Environment Detection
class MyConfig(DjangoConfig):
@property
def debug(self) -> bool:
return self._environment == "development"
@property
def database_url(self) -> str:
if self._environment == "production":
return "postgresql://prod-server/main"
return "sqlite:///db/dev.sqlite3"With Services
from django_cfg.models import EmailConfig, TelegramConfig, CacheConfig
class MyConfig(DjangoConfig):
secret_key: str = "your-secret-key-here-minimum-50-characters-long-string"
debug: bool = False
project_name: str = "My Project"
# Services
email: EmailConfig = EmailConfig(
backend="smtp",
host="smtp.gmail.com",
port=587,
username="[email protected]",
password="your-app-password"
)
telegram: TelegramConfig = TelegramConfig(
bot_token="your-bot-token",
allowed_user_ids=[123456789]
)
# Cache
cache_default: CacheConfig = CacheConfig(
redis_url="redis://localhost:6379/0"
)With Built-in Apps
class MyConfig(DjangoConfig):
secret_key: str = "your-secret-key-here-minimum-50-characters-long-string"
debug: bool = True
project_name: str = "My Project"
# Enable built-in apps
enable_accounts: bool = True
enable_agents: bool = True
enable_knowbase: bool = TrueModel Configuration
The model_config dict controls Pydantic behavior:
model_config = ConfigDict(
validate_assignment=True, # Validate on attribute assignment
extra="allow", # Allow extra fields
env_prefix="DJANGO_", # Environment variable prefix
populate_by_name=True, # Allow field name or alias
validate_default=True, # Validate default values
)Environment Variables
With env_prefix="DJANGO_", you can override any field:
export DJANGO_SECRET_KEY="new-secret-key"
export DJANGO_DEBUG=false
export DJANGO_PROJECT_NAME="My App"Extra Fields
With extra="allow", you can add custom fields:
config = MyConfig(
secret_key="...",
debug=True,
custom_field="custom_value" # Allowed
)See Also
- Configuration Overview - Configuration system overview
- Security Settings - Security configuration
- Database Models - Database configuration
- Cache Models - Cache configuration
- Email Models - Email configuration