100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Python

Django Settings and Environments

Understand how Django's settings module works and how to structure configuration safely across local, staging, and production environments.

Testing & DeploymentIntermediate9 min readJul 10, 2026
Analogies

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.

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

Was this page helpful?

Topics covered

#Python#DjangoStudyNotes#WebDevelopment#DjangoSettingsAndEnvironments#Django#Settings#Environments#Splitting#StudyNotes#SkillVeris