Backend / Web frameworks / Django / 10_settings_py.md

settings.py

Updated 6 interview angles 3 min read source
On this page6
  1. The layout that survives contact with production
  2. Secrets come from the environment, and are validated
  3. The production settings that are not optional
  4. What changed recently
  5. Settings you will actually tune
  6. Interview angle

settings.py

Django’s configuration is a Python module, which is its strength and its trap. It can compute values, import things and branch — so the discipline is entirely yours. As of 2026-08 the current release is Django 6.1, with 5.2 as the LTS.

The layout that survives contact with production

A single settings.py with if DEBUG: branches is the thing to move away from. Split by environment, share a base:

text
config/settings/
    base.py      everything common
    dev.py       from .base import *
    prod.py      from .base import *
    test.py      from .base import *
python
# manage.py / wsgi.py pick one via the environment.
os.environ.setdefault(
    "DJANGO_SETTINGS_MODULE", "config.settings.dev"
)

The property that matters: production settings are a file you can read, not a set of conditionals you have to evaluate in your head. When something is wrong in production you open prod.py and see the actual values.

Secrets come from the environment, and are validated

python
import os

def required(name: str) -> str:
    try:
        return os.environ[name]
    except KeyError:
        raise ImproperlyConfigured(f"{name} is not set")

SECRET_KEY = required("DJANGO_SECRET_KEY")
DATABASES = {"default": dj_database_url.parse(
    required("DATABASE_URL"), conn_max_age=600,
)}

required is the whole idea: a missing variable crashes at boot, where the deploy pipeline sees it, rather than at the first request that happens to touch that code path at 3am.

Gotcha: os.environ.get("DEBUG", False) is always truthy in production, because the environment gives you the string "False". Parse booleans explicitly — os.environ.get("DEBUG", "") == "1" — or use a settings library that types them.

The production settings that are not optional

python
DEBUG = False
ALLOWED_HOSTS = required("ALLOWED_HOSTS").split(",")

SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31_536_000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
CSRF_TRUSTED_ORIGINS = ["https://app.example.com"]

DEBUG = True in production is the classic incident: the error page renders a full traceback, local variables, and the settings module — including anything you did not think of as a secret. ALLOWED_HOSTS is the Host-header defence, and Django refuses to start without it once DEBUG is off.

You do not have to remember this list. Django will tell you:

bash
python manage.py check --deploy --settings=config.settings.prod

Run it in CI against the production settings module. It is a security review that costs one line of pipeline.

What changed recently

Setting Status
USE_L10N removed in 5.0 — always on
USE_TZ defaults to True since 5.0
DEFAULT_AUTO_FIELD must be set; BigAutoField
STORAGES replaced DEFAULT_FILE_STORAGE in 4.2

Naming USE_L10N or the old storage settings in an interview dates you the same way any stale API does.

Settings you will actually tune

  • CONN_MAX_AGE — persistent database connections. 0 reconnects per request, which is a measurable cost under load and a disaster behind pgbouncer in transaction mode. See Databases.
  • CACHES — Redis for sessions and the cache, sized deliberately.
  • LOGGING — structured JSON to stdout in a container, not a file path.
  • STORAGES — S3 or a CDN for media in production; staticfiles is a separate backend from default file storage.

Interview angle 6

  • “How do you manage settings across environments?” - one base module with per-environment files that import it, values from the environment, and validation at startup. Production settings should be a file you can read rather than conditionals you evaluate in your head.
  • “What must never be wrong in production?” - DEBUG = False, since the debug page leaks tracebacks, locals and the settings module; a correct ALLOWED_HOSTS; a real SECRET_KEY from the environment; and the secure cookie plus HSTS settings.
  • “Why validate settings at startup?” - a missing variable should crash on boot where the deploy pipeline sees it, not on the first request that reaches that code path. A required() helper that raises ImproperlyConfigured is three lines.
  • “How would you check a deployment is configured safely?” - manage.py check --deploy against the production settings module, in CI. It flags the whole security checklist without anyone having to remember it.
  • “What’s the subtle environment-variable bug?” - environment values are strings, so "False" is truthy. DEBUG = os.environ.get("DEBUG", False) ships debug mode to production, and the code looks correct.
  • “What does CONN_MAX_AGE do?” - keeps database connections open across requests instead of reconnecting each time. Worth setting under load, and worth setting back to 0 behind a transaction-mode connection pooler, which does the pooling for you.