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

Essential 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    pass

ALLOWED_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.com

SECRET_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.1

Using 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/dbname

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

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

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

Caching

# 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_SIZE

Settings 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_PAGE

Bà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 correctly

Bà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_settings

Bà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