Skip to Content

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:

  1. Environment Detection - Detects current environment (dev/staging/prod)
  2. Path Resolution - Finds project root, detects URLs/WSGI
  3. Smart Defaults - Applies environment-aware defaults
  4. 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_settings

Example 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 apps

get_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 middleware

get_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_hosts

Validation

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 = True

Model 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