Django Settings and Environments
Every Django project has a settings.py module (referenced via the DJANGO_SETTINGS_MODULE environment variable) that defines everything from installed apps and middleware to database connections and static file handling. Because the same codebase typically runs in local development, CI, staging, and production with very different configuration needs, hardcoding a single settings.py quickly becomes unmanageable — the standard fix is to split settings by environment and inject environment-specific values, such as database credentials and API keys, through environment variables rather than committing them to source control.
Cricket analogy: It's like a team using different match-day XIs for a Test match in Perth versus a T20 in Mumbai — same core squad and philosophy (codebase), but the lineup (settings) is tuned to the specific conditions.
Splitting Settings by Environment
A common pattern is to create a settings/ package with base.py holding shared configuration (INSTALLED_APPS, MIDDLEWARE, templates), and dev.py, staging.py, and prod.py each importing everything from base via from .base import * and then overriding what differs, such as DEBUG, DATABASES, ALLOWED_HOSTS, and caching backends. You then point DJANGO_SETTINGS_MODULE at the right module for each environment (e.g., myproject.settings.prod in your production wsgi.py or deployment environment variables), which keeps environment-specific logic out of a single sprawling file and makes it obvious at a glance what differs between environments.
Cricket analogy: It's like a cricket academy having a shared 'fundamentals' curriculum (base.py) that every age group uses, but the U19 program and the senior pro program each layer on their own specialized modules on top.
# settings/base.py
from pathlib import Path
import environ
BASE_DIR = Path(__file__).resolve().parent.parent.parent
env = environ.Env()
environ.Env.read_env(BASE_DIR / ".env")
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"blog",
]
SECRET_KEY = env("DJANGO_SECRET_KEY")
# settings/prod.py
from .base import * # noqa
DEBUG = False
ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS")
DATABASES = {
"default": env.db("DATABASE_URL"),
}
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
Environment Variables and Secrets
Libraries like django-environ or python-decouple read configuration from a .env file locally and from real environment variables in deployed environments, giving you typed accessors like env("DJANGO_SECRET_KEY"), env.bool("DEBUG", default=False), and env.db("DATABASE_URL") that parse a full database URL into Django's DATABASES dict format. The .env file itself must be listed in .gitignore and never committed, since it typically contains database passwords, third-party API keys, and the Django SECRET_KEY used to sign sessions and password reset tokens.
Cricket analogy: It's like a team's confidential scouting reports on an opponent's weaknesses — invaluable to the coaching staff (your app) but never something you'd hand to a journalist (commit to a public repo).
django-environ's env.db() can parse a full DATABASE_URL string like postgres://user:pass@host:5432/dbname directly into Django's DATABASES dict format, which is especially convenient on hosting platforms (Heroku, Railway, Render) that inject a single DATABASE_URL environment variable.
DEBUG, ALLOWED_HOSTS, and SECRET_KEY
DEBUG = True should only ever be used locally: it enables the interactive debugger and detailed tracebacks, which in production would leak your source code, local variable values, and settings to anyone who triggers a 500 error. ALLOWED_HOSTS must list the exact domains Django is allowed to serve to prevent HTTP Host header attacks, and SECRET_KEY — used to sign session cookies, CSRF tokens, and password reset links — must be a long random value generated per environment and rotated if it's ever exposed, since anyone with the key can forge valid sessions.
Cricket analogy: Leaving DEBUG on in production is like leaving the dressing room door wide open during a match — anyone walking by can see the team's tactical whiteboard (source code and internals) that should stay private.
Never commit SECRET_KEY, database passwords, or API keys to version control, even temporarily. If a key does leak into git history, rotating it isn't optional — generate a new SECRET_KEY (which invalidates existing sessions) and revoke the exposed API credentials immediately.
- DJANGO_SETTINGS_MODULE points at the settings module to use; splitting into base/dev/staging/prod files keeps environment differences explicit.
- django-environ and python-decouple read typed configuration from .env files locally and real environment variables in deployment.
- .env files must be gitignored — never commit SECRET_KEY, database passwords, or API keys to source control.
- DEBUG must be False in production; leaving it True leaks tracebacks, source code, and settings to attackers.
- ALLOWED_HOSTS must list only the real domains serving the app to prevent Host header attacks.
- SECRET_KEY signs sessions, CSRF tokens, and password reset links; rotate it immediately if ever exposed.
- env.db() can parse a single DATABASE_URL string into Django's DATABASES dict, which many hosting platforms provide out of the box.
Practice what you learned
1. What is the purpose of splitting settings into base.py, dev.py, and prod.py?
2. Why should .env files never be committed to version control?
3. What risk does leaving DEBUG = True in production create?
4. What does ALLOWED_HOSTS protect against?
5. What should you do if SECRET_KEY is accidentally committed to a public repository?
Was this page helpful?
You May Also Like
Deploying a Django App
Walk through the practical steps of taking a Django project from `runserver` to a production-ready deployment with gunicorn, nginx, and a managed database.
Testing Django Applications
Learn how Django's built-in test framework, test client, and fixtures let you verify models, views, and forms with confidence before shipping.
Django Quick Reference
A cheat-sheet style tour of the manage.py commands, ORM syntax, and settings you reach for most often when building and shipping a Django project.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics