Settings and Multi-Environment Configuration

Introduction

config/settings.py controls almost every runtime behavior: debug mode, hosts, database, templates, and secrets. This chapter explains the most important settings for development, shows how to load values from .env, and splits configuration into base / development / production modules—the pattern teams use before deploying with Gunicorn and Nginx.

Prerequisites

Core Settings Explained

SECRET_KEY

Used for signing sessions, CSRF tokens, and password reset links.

python
SECRET_KEY = "django-insecure-..."  # auto-generated by startproject
EnvironmentRule
DevelopmentDefault insecure key is OK locally only
ProductionLong random string from env var; never commit

Generate a new key (example):

bash
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"

DEBUG

python
DEBUG = True
DEBUGBehavior
TrueDetailed error pages with traceback; static files served simply in dev
FalseGeneric error pages; requires ALLOWED_HOSTS and proper logging

Warning

Never Ship DEBUG=True

Public sites with DEBUG=True leak settings and code paths. Production must use DEBUG=False.

ALLOWED_HOSTS

python
ALLOWED_HOSTS = []

When DEBUG=False, Django rejects requests whose Host header is not listed.

Examples:

python
ALLOWED_HOSTS = ["example.com", "www.example.com"]
ALLOWED_HOSTS = [".example.com"]  # subdomains

With DEBUG=True, empty list is allowed for localhost / 127.0.0.1.

INSTALLED_APPS and MIDDLEWARE

Already touched in earlier chapters. Adding a third-party package usually means:

  1. pip install package
  2. Add app string to INSTALLED_APPS
  3. Add middleware if docs require (order matters)

DATABASES

Default development config:

python
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "db.sqlite3",
    }
}

MySQL production example (preview—full setup in ORM chapters):

python
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": "mydb",
        "USER": "myuser",
        "PASSWORD": os.environ.get("DB_PASSWORD", ""),
        "HOST": "127.0.0.1",
        "PORT": "3306",
        "OPTIONS": {"charset": "utf8mb4"},
    }
}

Link: MySQL track.

TEMPLATES

  • DIRS — project-wide template folders
  • APP_DIRS — find app/templates/ in each installed app

STATIC_URL and MEDIA (preview)

python
STATIC_URL = "static/"
# MEDIA_URL = "media/"
# MEDIA_ROOT = BASE_DIR / "media"

MEDIA_* for user uploads—configured fully in chapter 16.

Internationalization

python
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True

Use TIME_ZONE = "Asia/Shanghai" (or your zone) when displaying local times—keep USE_TZ = True for aware datetimes.

Load Settings from Environment Variables

Install python-dotenv (development convenience):

bash
pip install python-dotenv

Add to requirements.txt.

Create .env in project root (already in .gitignore):

env
DJANGO_SECRET_KEY=your-dev-secret-key-here
DJANGO_DEBUG=True
DATABASE_URL=sqlite:///db.sqlite3

At the top of settings (before split refactor, or in base.py):

python
import os
from pathlib import Path
 
from dotenv import load_dotenv
 
load_dotenv(BASE_DIR / ".env")
 
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
DEBUG = os.environ.get("DJANGO_DEBUG", "False") == "True"

Code explanation:

  • load_dotenv reads .env into os.environ
  • Production servers set env vars in systemd/Docker instead of files—same os.environ access

Tip

Fail Fast on Missing Secrets

Use os.environ["DJANGO_SECRET_KEY"] in production so misconfiguration crashes at startup, not silently with an insecure default.

Split Settings: base / development / production

Replace single config/settings.py with a package:

text
config/
├── settings/
│   ├── __init__.py
│   ├── base.py
│   ├── development.py
│   └── production.py
├── urls.py
├── wsgi.py
└── asgi.py

config/settings/base.py

Shared defaults:

config/settings/development.py

python
from .base import *
 
SECRET_KEY = "dev-only-insecure-key"
DEBUG = True
ALLOWED_HOSTS = ["127.0.0.1", "localhost"]
 
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "db.sqlite3",
    }
}

config/settings/production.py

config/settings/init.py

Empty or import development by default for local work:

python
from .development import *

Point manage.py and wsgi at the right module

manage.py (development default):

python
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.development")

config/wsgi.py (production):

python
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")

Run with explicit module (without editing files):

bash
DJANGO_SETTINGS_MODULE=config.settings.production python manage.py check

Windows PowerShell:

powershell
$env:DJANGO_SETTINGS_MODULE="config.settings.production"
python manage.py check

Verify Configuration

bash
python manage.py check
python manage.py check --deploy

--deploy warns about DEBUG, SECRET_KEY, ALLOWED_HOSTS, HTTPS settings—useful before production.

Settings Quick Reference

SettingDev typicalProd typical
DEBUGTrueFalse
SECRET_KEYLocal / .envStrong env var
ALLOWED_HOSTSlocalhost, 127.0.0.1Your domain(s)
DATABASESSQLite fileMySQL/PostgreSQL
HTTPSOptionalNginx + TLS

Common Mistakes

MistakeSymptomFix
DEBUG=True on VPSSettings leak in browserproduction.py with DEBUG=False
Empty ALLOWED_HOSTS in prodDisallowedHostAdd domain
Committed .envSecret leakRotate keys; use .gitignore
Wrong DJANGO_SETTINGS_MODULEWrong DB or debug flagCheck manage.py, systemd Environment
Editing base.py for one envProd/dev couplingOverride in development.py / production.py only

Post-Chapter Checklist

  • You can explain SECRET_KEY, DEBUG, ALLOWED_HOSTS
  • You know default SQLite DATABASES layout
  • You created .env (gitignored) or understand env vars
  • You understand base / development / production split (optional but recommended)
  • python manage.py check passes

FAQ

Keep single settings.py for learning?

Yes initially. Split when you deploy or feel if DEBUG branches piling up.

django-environ vs python-dotenv?

Both work. django-environ parses DATABASE_URL strings; python-dotenv is minimal—fine for this track.

Where set settings on Gunicorn?

Environment= or EnvironmentFile= in systemd; DJANGO_SETTINGS_MODULE=config.settings.production.

Change LANGUAGE_CODE to Chinese?

LANGUAGE_CODE = "zh-hans" — also translate UI strings with Django i18n (advanced).

Is SQLite enough for production?

For tiny low-traffic sites, maybe. Most production Django sites use MySQL or PostgreSQL.

Commit settings/production.py?

Yes—the file should contain no secrets, only os.environ lookups.