Django

Settings — the basics

The one file that configures an entire Django project — the handful of settings you'll actually touch constantly, and why secrets don't belong inside it directly.

Beginner

3 min read

settings.py: one file, read once, controlling everything

Every Django project has a settings.py file — a plain Python module where every project-wide configuration lives: which apps are installed, how to connect to the database, where templates and static files live, security settings, and much more. It's read once when the project starts, and its values are available throughout the project via from django.conf import settings.

INSTALLED_APPS: what's actually part of this project

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "myapp",                        # your own app
]

This list determines which apps Django actually knows about — an app's models won't be picked up by makemigrations, its templates won't be found, and its admin registrations won't run, unless it's listed here. The django.contrib.* entries are Django's own built-in apps (the admin, the authentication system, and others) — included by default in every new project, since most projects need at least some of them.

DATABASES: how to connect to the database

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "mydb",
        "USER": "myuser",
        "PASSWORD": "mypassword",
        "HOST": "localhost",
        "PORT": "5432",
    }
}

This tells Django which database engine to use and how to connect to it — ENGINE selects PostgreSQL, MySQL, SQLite, or another supported backend, and the rest are standard connection details. New projects default to SQLite (a simple, file-based database requiring zero setup, ENGINE: "django.db.backends.sqlite3"), which is genuinely fine for local development and small projects, but real production deployments almost always switch to PostgreSQL or MySQL for the concurrency and features a real database server provides.

DEBUG: the setting that must never be True in production

DEBUG = True    # fine for local development
DEBUG = False   # required in production

With DEBUG = True, an unhandled error shows a detailed page with the full traceback, local variable values, and settings — extremely useful while developing, and extremely dangerous in production, where it hands potentially sensitive internal information (database credentials, secret keys, file paths, source code) to anyone who triggers an error. DEBUG = False in production is one of the most well-known, non-negotiable Django deployment rules for exactly this reason — Django itself will refuse to run at all in some configurations if DEBUG = False and ALLOWED_HOSTS isn't also set correctly, specifically to keep this mistake from shipping silently.

Why secrets don't belong directly in settings.py

import os
SECRET_KEY = os.environ["SECRET_KEY"]
DATABASES = {"default": {"PASSWORD": os.environ["DB_PASSWORD"], ...}}

settings.py is a regular Python file, typically committed to version control along with the rest of the project's code — which makes it exactly the wrong place to hardcode a database password, API key, or SECRET_KEY (a value Django uses internally for cryptographic signing, including session security). Reading secrets from environment variables instead (os.environ["SECRET_KEY"]) keeps the actual secret values out of the committed codebase entirely — the code that reads the secret is committed and shared; the actual secret value lives only in each environment's configuration (a local .env file excluded from git, or the hosting platform's environment variable settings), never in a file that ends up in git history.

ALLOWED_HOSTS: which domains this server will actually respond to

ALLOWED_HOSTS = ["example.com", "www.example.com"]

This is a security check, not a routing feature: Django refuses to serve a response if the incoming request's Host header doesn't match an entry in this list, when DEBUG = False. It exists specifically to prevent a class of attack (HTTP Host header poisoning) where a malicious request claims to be for a different domain than the server actually serves, potentially tricking password-reset emails or cache keys built from the host header into containing an attacker-controlled domain.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. What breaks if an app is missing from INSTALLED_APPS?

2. Why is DEBUG = True dangerous in production specifically?

3. Why shouldn't a database password be hardcoded directly in settings.py?

4. What does ALLOWED_HOSTS actually protect against?