Bài 18: Django Settings
Django Settings Overview
Django Settings là file cấu hình chính của Django project, định nghĩa tất cả settings và configuration.
# settings.py location:myproject/ myproject/ __init__.py settings.py # Main configuration file urls.py wsgi.py asgi.py manage.py # settings.py contains:# - DEBUG mode# - ALLOWED_HOSTS# - INSTALLED_APPS# - MIDDLEWARE# - DATABASES# - SECRET_KEY# - TEMPLATES# - STATIC/MEDIA files# - Email configuration# - Internationalization# - Security settings# - Custom settings # Access settings in code:from django.conf import settings if settings.DEBUG: print("Debug mode enabled") database_name = settings.DATABASES['default']['NAME']Settings Module Structure
# Basic settings.py structure: # Build pathsimport osfrom pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent # SecuritySECRET_KEY = 'your-secret-key-here'DEBUG = TrueALLOWED_HOSTS = [] # Application definitionINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # Your apps 'myapp',] 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 = 'myproject.urls' TEMPLATES = [...]DATABASES = {...}STATIC_URL = '/static/' # InternationalizationLANGUAGE_CODE = 'en-us'TIME_ZONE = 'UTC'USE_I18N = TrueUSE_TZ = TrueEssential Settings
DEBUG Mode
# DEBUG = True: Development mode# - Detailed error pages# - Shows traceback# - Serves static files# - Security checks disabled # DEBUG = False: Production mode# - Generic error pages (404.html, 500.html)# - No traceback# - Must configure static files serving# - Security checks enabled # settings.pyDEBUG = True # Development# DEBUG = False # Production # Use environment variableimport osDEBUG = os.environ.get('DEBUG', 'False') == 'True' # Check DEBUG in codefrom django.conf import settings if settings.DEBUG: print("Running in debug mode") # Enable debugging featureselse: # Production mode passALLOWED_HOSTS
# List of host/domain names this site can serve# Required when DEBUG = False # DevelopmentALLOWED_HOSTS = [] # Allow any host when DEBUG = True # Production - specify allowed domainsALLOWED_HOSTS = [ 'example.com', 'www.example.com', '192.168.1.100',] # Allow all subdomainsALLOWED_HOSTS = [ '.example.com', # Matches example.com and all subdomains] # From environment variableALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',') # Example .env file:# ALLOWED_HOSTS=example.com,www.example.com,api.example.comSECRET_KEY
# Used for cryptographic signing# - Sessions# - Cookies# - CSRF tokens# - Password reset tokens # IMPORTANT: Keep secret! Never commit to version control! # Bad - hardcoded (development only)SECRET_KEY = 'django-insecure-dev-key-12345' # Good - from environment variableimport osSECRET_KEY = os.environ.get('SECRET_KEY') # Generate new secret keyfrom django.core.management.utils import get_random_secret_keyprint(get_random_secret_key()) # Or using Pythonimport secretsprint(secrets.token_urlsafe(50)) # Check if secret key is setif not SECRET_KEY: raise ValueError("SECRET_KEY must be set in environment")DATABASES
# Default - SQLite (development)DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', }} # PostgreSQL (production recommended)DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'mydatabase', 'USER': 'mydatabaseuser', 'PASSWORD': 'mypassword', 'HOST': 'localhost', 'PORT': '5432', 'CONN_MAX_AGE': 600, # Connection pooling 'OPTIONS': { 'connect_timeout': 10, } }} # MySQLDATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'mydatabase', 'USER': 'mydatabaseuser', 'PASSWORD': 'mypassword', 'HOST': 'localhost', 'PORT': '3306', 'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", } }} # From environment variablesDATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('DB_NAME'), 'USER': os.environ.get('DB_USER'), 'PASSWORD': os.environ.get('DB_PASSWORD'), 'HOST': os.environ.get('DB_HOST', 'localhost'), 'PORT': os.environ.get('DB_PORT', '5432'), }} # Multiple databasesDATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'primary_db', # ... }, 'users': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'users_db', # ... }, 'analytics': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'analytics_db', # ... }}Environment Variables
Using python-decouple
# Install# pip install python-decouple # settings.pyfrom decouple import config, Csv # Basic usageSECRET_KEY = config('SECRET_KEY')DEBUG = config('DEBUG', default=False, cast=bool) # With type castingDATABASE_PORT = config('DATABASE_PORT', default=5432, cast=int)ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=Csv()) # Database configurationDATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': config('DB_NAME'), 'USER': config('DB_USER'), 'PASSWORD': config('DB_PASSWORD'), 'HOST': config('DB_HOST', default='localhost'), 'PORT': config('DB_PORT', default=5432, cast=int), }} # .env file (in project root, not in git)SECRET_KEY=your-secret-key-hereDEBUG=TrueDB_NAME=mydatabaseDB_USER=myuserDB_PASSWORD=mypasswordDB_HOST=localhostDB_PORT=5432ALLOWED_HOSTS=localhost,127.0.0.1Using django-environ
# Install# pip install django-environ # settings.pyimport environ env = environ.Env( # Set casting and default values DEBUG=(bool, False), ALLOWED_HOSTS=(list, []),) # Read .env fileenviron.Env.read_env(BASE_DIR / '.env') # Access variablesSECRET_KEY = env('SECRET_KEY')DEBUG = env('DEBUG')ALLOWED_HOSTS = env.list('ALLOWED_HOSTS') # Database URLDATABASES = { 'default': env.db()} # .env fileSECRET_KEY=your-secret-keyDEBUG=TrueALLOWED_HOSTS=localhost,127.0.0.1DATABASE_URL=postgresql://user:password@localhost:5432/dbnameManual Environment Variables
# settings.pyimport os # Get with defaultSECRET_KEY = os.environ.get('SECRET_KEY', 'dev-secret-key')DEBUG = os.environ.get('DEBUG', 'False') == 'True' # Required variable (raises error if missing)SECRET_KEY = os.environ['SECRET_KEY'] # Type conversionDATABASE_PORT = int(os.environ.get('DATABASE_PORT', 5432)) # List from comma-separated stringALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',') # Set environment variables:# Linux/Mac:# export SECRET_KEY="your-secret-key"# export DEBUG=True # Windows:# set SECRET_KEY=your-secret-key# set DEBUG=TrueMultiple Environment Settings
Split Settings by Environment
# Project structure:myproject/ settings/ __init__.py base.py # Common settings development.py # Dev-specific production.py # Prod-specific test.py # Test-specific # settings/base.py - Common settingsfrom pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent.parent INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', # ... 'myapp',] MIDDLEWARE = [...] # settings/development.pyfrom .base import * DEBUG = TrueALLOWED_HOSTS = ['localhost', '127.0.0.1'] DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', }} # Development toolsINSTALLED_APPS += [ 'django_extensions', 'debug_toolbar',] MIDDLEWARE += [ 'debug_toolbar.middleware.DebugToolbarMiddleware',] # settings/production.pyfrom .base import *import os DEBUG = FalseALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS').split(',') DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('DB_NAME'), 'USER': os.environ.get('DB_USER'), 'PASSWORD': os.environ.get('DB_PASSWORD'), 'HOST': os.environ.get('DB_HOST'), 'PORT': os.environ.get('DB_PORT', 5432), }} # Security settingsSECURE_SSL_REDIRECT = TrueSESSION_COOKIE_SECURE = TrueCSRF_COOKIE_SECURE = True # Use settings:# Development:# python manage.py runserver --settings=myproject.settings.development # Production:# export DJANGO_SETTINGS_MODULE=myproject.settings.production# Or in wsgi.py:os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings.production')Conditional Settings
# settings.pyimport os DEBUG = os.environ.get('DEBUG', 'False') == 'True' if DEBUG: # Development settings DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } # Development tools INSTALLED_APPS += ['debug_toolbar'] MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware'] else: # Production settings DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', # ... from environment variables } } # Security settings SECURE_SSL_REDIRECT = True SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = TrueImportant Settings
Static and Media Files
# Static files (CSS, JavaScript, Images)STATIC_URL = '/static/'STATIC_ROOT = BASE_DIR / 'staticfiles'STATICFILES_DIRS = [ BASE_DIR / 'static',] # Media files (user uploads)MEDIA_URL = '/media/'MEDIA_ROOT = BASE_DIR / 'media' # Production - use CDN or cloud storageif not DEBUG: # AWS S3 DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' STATICFILES_STORAGE = 'storages.backends.s3boto3.S3StaticStorage' AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME')Email Configuration
# Console backend (development)EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # SMTP backend (production)EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'EMAIL_HOST = 'smtp.gmail.com'EMAIL_PORT = 587EMAIL_USE_TLS = TrueEMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')DEFAULT_FROM_EMAIL = '[email protected]' # SendGridEMAIL_HOST = 'smtp.sendgrid.net'EMAIL_HOST_USER = 'apikey'EMAIL_HOST_PASSWORD = os.environ.get('SENDGRID_API_KEY')EMAIL_PORT = 587EMAIL_USE_TLS = TrueCaching
# Development - dummy cacheCACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', }} # Production - RedisCACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.redis.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', }, 'KEY_PREFIX': 'myapp', 'TIMEOUT': 300, }} # MemcachedCACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', 'LOCATION': '127.0.0.1:11211', }}Logging
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '{levelname} {asctime} {module} {message}', 'style': '{', }, }, 'handlers': { 'console': { 'class': 'logging.StreamHandler', 'formatter': 'verbose', }, 'file': { 'class': 'logging.FileHandler', 'filename': BASE_DIR / 'logs/django.log', 'formatter': 'verbose', }, }, 'loggers': { 'django': { 'handlers': ['console', 'file'], 'level': 'INFO', }, 'myapp': { 'handlers': ['console', 'file'], 'level': 'DEBUG', }, },}Security Settings
# Production security settingsif not DEBUG: # HTTPS SECURE_SSL_REDIRECT = True SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') # HSTS SECURE_HSTS_SECONDS = 31536000 # 1 year SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_HSTS_PRELOAD = True # Cookies SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True CSRF_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = 'Lax' CSRF_COOKIE_SAMESITE = 'Lax' # Headers SECURE_CONTENT_TYPE_NOSNIFF = True SECURE_BROWSER_XSS_FILTER = True X_FRAME_OPTIONS = 'DENY'Custom Settings
Adding Custom Settings
# settings.py# Custom application settingsAPP_NAME = 'My Application'APP_VERSION = '1.0.0'MAX_UPLOAD_SIZE = 5 * 1024 * 1024 # 5MB # Feature flagsFEATURE_NEW_UI = TrueFEATURE_ANALYTICS = False # API settingsAPI_RATE_LIMIT = 100 # requests per minuteAPI_KEY = os.environ.get('API_KEY') # Payment settingsSTRIPE_PUBLIC_KEY = os.environ.get('STRIPE_PUBLIC_KEY')STRIPE_SECRET_KEY = os.environ.get('STRIPE_SECRET_KEY') # Access in codefrom django.conf import settings if settings.FEATURE_NEW_UI: template = 'new_ui/page.html'else: template = 'old_ui/page.html' max_size = settings.MAX_UPLOAD_SIZESettings Class Pattern
# settings.pyclass Config: """Application configuration""" # General APP_NAME = 'MyApp' APP_VERSION = '1.0.0' # Pagination ITEMS_PER_PAGE = 20 MAX_PAGE_SIZE = 100 # File upload MAX_UPLOAD_SIZE = 5 * 1024 * 1024 ALLOWED_EXTENSIONS = ['.jpg', '.png', '.pdf'] # Cache timeouts CACHE_TIMEOUT_SHORT = 300 # 5 minutes CACHE_TIMEOUT_MEDIUM = 1800 # 30 minutes CACHE_TIMEOUT_LONG = 3600 # 1 hour # Use in codefrom django.conf import settings items_per_page = settings.Config.ITEMS_PER_PAGEBài Tập
Bài 1: Multi-Environment Setup
Task: Cấu hình multiple environments:
# Create settings structure:# settings/# __init__.py# base.py # Common settings# development.py # Dev settings# staging.py # Staging settings# production.py # Production settings # Requirements:# 1. base.py: Common settings for all environments# 2. development.py:# - DEBUG = True# - SQLite database# - Console email backend# - Django Debug Toolbar# 3. staging.py:# - DEBUG = True# - PostgreSQL database# - Real email (test server)# 4. production.py:# - DEBUG = False# - PostgreSQL (from env vars)# - Real email (production)# - All security settings# - Redis cache# - Static files on S3 # Test each environment works correctlyBài 2: Environment Variables
Task: Implement environment-based configuration:
# Use python-decouple or django-environ # Requirements:# 1. Create .env.example template# 2. All secrets from environment variables:# - SECRET_KEY# - Database credentials# - Email credentials# - API keys (Stripe, AWS, etc.)# 3. Type casting for:# - DEBUG (boolean)# - DATABASE_PORT (integer)# - ALLOWED_HOSTS (list)# 4. Default values for optional settings# 5. Validation (raise error if required vars missing)# 6. Documentation in .env.example # .env.example format:# SECRET_KEY=your-secret-key-here# DEBUG=True# DB_NAME=mydb# # etc...Bài 3: Settings Validator
Task: Create settings validation:
# Create management command to validate settings # Requirements:# 1. Check required settings exist:# - SECRET_KEY# - DATABASES# - ALLOWED_HOSTS (if DEBUG=False)# 2. Validate SECRET_KEY:# - Not default/insecure key# - Minimum length# 3. Security checks:# - If DEBUG=False, all security settings enabled# - Cookies secure on HTTPS# 4. Database connection test# 5. Email configuration test# 6. Cache connection test# 7. Output report:# - Passed checks (green)# - Warnings (yellow)# - Failed checks (red) # Usage:# python manage.py validate_settingsBài 4: Dynamic Settings
Task: Implement database-backed settings:
# Create system for runtime-configurable settings # Requirements:# 1. Model to store settings:# - key (unique)# - value (JSON field)# - description# - is_active# 2. Settings manager:# - Get setting with default# - Set setting# - Cache settings# 3. Admin interface:# - View all settings# - Edit settings# - Group by category# 4. Example settings:# - SITE_NAME# - MAINTENANCE_MODE# - MAX_UPLOAD_SIZE# - ITEMS_PER_PAGE# 5. Middleware to load settings# 6. Template tag to access settings # Usage:# {% load settings_tags %}# {{ setting.SITE_NAME }}Tài Liệu Tham Khảo
Previous: Bài 17: Middleware | Next: Bài 19: Generic Views