Module 2: Settings.py — The Configuration Engine
🎯 What You Will Learn in This Module
By the end of this tutorial, you will:
- Understand every major section of
settings.py - Learn to protect sensitive data using environment variables
- Configure PostgreSQL for development and production
- Master INSTALLED_APPS, MIDDLEWARE, and app registration
- Set up Django REST Framework, CORS, and Authentication
- Organize settings for development vs. production environments
- Build a secure, production-ready configuration
📖 1. What is settings.py?
settings.py is the brain of your Django project. Every behavior — from which database you use to how passwords are validated — is controlled here. Think of it as the control panel of your entire application.
When you created your project in Module 1, Django generated a default settings.py. Your job is to customize, secure, and extend it.
🏗️ 2. Project Root & Path Configuration
Open myproject/settings.py. The first lines define where your project lives on your computer.
1from pathlib import Path 2import os 3 4# Build paths inside the project like this: BASE_DIR / 'subdir'. 5BASE_DIR = Path(__file__).resolve().parent.parent
What is BASE_DIR?
BASE_DIR points to the root folder of your Django project — the folder that contains manage.py.
django-course/ ← BASE_DIR points here
├── manage.py
├── myproject/
│ └── settings.py ← __file__ is here
├── users/
├── blog/
└── api/
How it works:
__file__=myproject/settings.py.resolve()= get the absolute path.parent=myproject/folder.parent.parent= root project folder (django-course/)
Why this matters:
Whenever you need to reference a file or folder in your project (like templates, static files, or media uploads), you use BASE_DIR to ensure the path works on any computer (Windows, Mac, or Linux).
1# Example: Pointing to a templates folder 2TEMPLATES_DIR = BASE_DIR / 'templates' 3 4# Old way (still works but less clean) 5# TEMPLATES_DIR = os.path.join(BASE_DIR, 'templates')
💡 Tip:
pathlib.Pathis the modern Python way of handling paths. It automatically uses the correct slash (/or\) for your operating system.
🔐 3. Securing Your SECRET_KEY
The Problem
By default, Django generates a SECRET_KEY inside settings.py. This key is used to sign cookies, session data, and password reset tokens. If someone gets your SECRET_KEY, they can forge authentication tokens and hijack user sessions.
Never commit your SECRET_KEY to GitHub.
The Solution: Environment Variables
Create a .env file in your project root:
1# .env (create this file in your project root) 2 3DJANGO_SECRET_KEY=django-insecure-your-unique-key-here-change-me 4DJANGO_DEBUG=True 5DB_NAME=mydb 6DB_USER=postgres 7DB_PASSWORD=yourpassword 8DB_HOST=localhost 9DB_PORT=5432
Install a package to read this file:
1pip install python-decouple 2# OR 3pip install django-environ
Now update settings.py:
1from decouple import config 2 3SECRET_KEY = config('DJANGO_SECRET_KEY', default='fallback-dev-key-only')
How it works:
python-decouple reads the .env file and fetches values by name. If the environment variable isn't found, it uses the default value. In production, you set these as actual server environment variables instead of a .env file.
🚨 Critical: Add
.envto your.gitignorefile immediately:1echo ".env" >> .gitignore
🐛 4. DEBUG Mode — Development vs. Production
1DEBUG = config('DJANGO_DEBUG', default='True') == 'True'
What Does DEBUG Do?
| Mode | Behavior |
|---|---|
DEBUG = True | Shows detailed error pages with stack traces. Static files served automatically. |
DEBUG = False | Shows generic "Bad Request" pages. You must configure static file serving manually. |
Rule: DEBUG = True only on your local machine. Never in production.
Why the == 'True' trick?
Environment variables are always strings. config() returns 'True' (a string), but Django expects a boolean. Comparing it to 'True' converts it properly.
🌐 5. ALLOWED_HOSTS — Your Domain Bodyguard
1ALLOWED_HOSTS = [ 2 'localhost', 3 '127.0.0.1', 4 '.yourdomain.com', # Allows www.yourdomain.com and blog.yourdomain.com 5]
What is ALLOWED_HOSTS?
Django uses this list to verify which domain names or IP addresses are allowed to serve your project. If a request comes from a domain not in this list, Django rejects it with a 400 Bad Request error.
Why it matters:
This prevents HTTP Host Header attacks, where attackers trick your server into responding to malicious domains.
For development: Keep localhost and 127.0.0.1.
For production: Add your actual domain:
1ALLOWED_HOSTS = ['myblog.com', 'www.myblog.com']
📦 6. INSTALLED_APPS — Registering Your Components
1INSTALLED_APPS = [ 2 # ==================== Django Built-in Apps ==================== 3 'django.contrib.admin', # Admin panel interface 4 'django.contrib.auth', # User authentication system 5 'django.contrib.contenttypes', # Framework for content types 6 'django.contrib.sessions', # Session management (login state) 7 'django.contrib.messages', # Flash messages (one-time notifications) 8 'django.contrib.staticfiles', # Static file serving (CSS, JS, images) 9 10 # ==================== Third-Party Packages ==================== 11 'rest_framework', # Django REST Framework (API toolkit) 12 'rest_framework.authtoken', # Token authentication for APIs 13 'corsheaders', # Cross-Origin Resource Sharing 14 15 # ==================== Your Local Apps ==================== 16 'users', # Authentication & user profiles 17 'blog', # Blog posts, categories, comments 18 'api', # REST API endpoints 19]
The Order Matters
Django processes apps in this order. A good convention is:
- Django built-ins first (they provide core functionality)
- Third-party packages next (they extend Django)
- Your apps last (they depend on the above)
What happens if you forget an app?
- Models won't be detected →
makemigrationsignores them - Admin won't show your models
- Templates and static files might not be found
- API endpoints won't work
🛡️ 7. MIDDLEWARE — The Request Pipeline
1MIDDLEWARE = [ 2 'corsheaders.middleware.CorsMiddleware', # Must be at the TOP 3 'django.middleware.security.SecurityMiddleware', # HTTPS, security headers 4 'django.contrib.sessions.middleware.SessionMiddleware', # Session handling 5 'django.middleware.common.CommonMiddleware', # URL normalization 6 'django.middleware.csrf.CsrfViewMiddleware', # CSRF protection 7 'django.contrib.auth.middleware.AuthenticationMiddleware', # User auth 8 'django.contrib.messages.middleware.MessageMiddleware', # Flash messages 9 'django.middleware.clickjacking.XFrameOptionsMiddleware', # Clickjacking protection 10]
What is Middleware?
Imagine middleware as a conveyor belt of security guards and assistants. Every HTTP request (from the browser) and every HTTP response (back to the browser) passes through this pipeline.
Request → [CORS] → [Security] → [Session] → [Common] → [CSRF] → [Auth] → View
↑
Response ← [Security] ← [Session] ← [Common] ← [Messages] ← [XFrame] ←
Key Middleware Explained
| Middleware | What It Does |
|---|---|
CorsMiddleware | Allows your API to accept requests from other domains (e.g., React frontend). Must be first. |
SecurityMiddleware | Enforces HTTPS, adds security headers like HSTS. |
SessionMiddleware | Attaches session data to requests (keeps users logged in). |
CommonMiddleware | Adds trailing slashes, handles APPEND_SLASH. |
CsrfViewMiddleware | Protects forms from Cross-Site Request Forgery attacks. |
AuthenticationMiddleware | Adds request.user to every request. |
MessageMiddleware | Enables one-time flash messages ("Post created successfully!"). |
XFrameOptionsMiddleware | Prevents your site from being embedded in malicious iframes. |
⚠️ Warning: The order is critical.
SessionMiddlewaremust come beforeAuthenticationMiddlewarebecause authentication depends on sessions.
🗄️ 8. Database Configuration
Default: SQLite (Development)
Django ships with SQLite configured by default — a file-based database perfect for learning:
1DATABASES = { 2 'default': { 3 'ENGINE': 'django.db.backends.sqlite3', 4 'NAME': BASE_DIR / 'db.sqlite3', 5 } 6}
Production: PostgreSQL
For real applications, you need PostgreSQL. First, install the adapter:
1pip install psycopg2-binary
Then update settings.py:
1DATABASES = { 2 'default': { 3 'ENGINE': 'django.db.backends.postgresql', 4 'NAME': config('DB_NAME', default='mydb'), 5 'USER': config('DB_USER', default='postgres'), 6 'PASSWORD': config('DB_PASSWORD', default='password'), 7 'HOST': config('DB_HOST', default='localhost'), 8 'PORT': config('DB_PORT', default='5432'), 9 } 10}
Database Settings Explained
| Setting | Description |
|---|---|
ENGINE | Which database backend to use (sqlite3, postgresql, mysql, oracle) |
NAME | Database name (or file path for SQLite) |
USER | Database username |
PASSWORD | Database password |
HOST | Database server address (localhost or IP) |
PORT | Connection port (5432 for PostgreSQL) |
Before you can use PostgreSQL, you must:
- Install PostgreSQL on your machine
- Create a database:
CREATE DATABASE mydb; - Create a user with privileges
- Run
python manage.py migrate
🔑 9. Password Validators
1AUTH_PASSWORD_VALIDATORS = [ 2 { 3 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 4 }, 5 { 6 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 7 'OPTIONS': {'min_length': 8} 8 }, 9 { 10 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 11 }, 12 { 13 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 14 }, 15]
What Do Validators Do?
These enforce password strength rules when users create or change passwords:
| Validator | Rule |
|---|---|
UserAttributeSimilarityValidator | Password cannot be too similar to username/email |
MinimumLengthValidator | Password must be at least 8 characters |
CommonPasswordValidator | Rejects common passwords like "password123" |
NumericPasswordValidator | Prevents entirely numeric passwords like "12345678" |
You can customize these or write your own validators. For stricter security, increase min_length to 12 or 14.
🌍 10. Internationalization & Time
1LANGUAGE_CODE = 'en-us' # Default language 2TIME_ZONE = 'UTC' # Default timezone 3USE_I18N = True # Enable internationalization 4USE_TZ = True # Store datetimes in UTC, convert to local time
What this means:
USE_TZ = Trueensures Django stores all times in UTC in the database, then converts to the user's local timezone when displaying. This prevents timezone bugs when your app serves users globally.
To change timezone:
1TIME_ZONE = 'Asia/Kolkata' # India 2# OR 3TIME_ZONE = 'America/New_York' # US East Coast
📁 11. Static Files vs. Media Files
Django handles two types of user-uploaded/served files:
Static Files
Files that are part of your project code — CSS, JavaScript, images, fonts.
1STATIC_URL = 'static/' # URL prefix for static files 2STATIC_ROOT = BASE_DIR / 'staticfiles' # Where collectstatic gathers files 3STATICFILES_DIRS = [BASE_DIR / 'static'] # Additional folders to search
How it works:
- During development: Django serves static files automatically (when
DEBUG = True) - During production: You run
python manage.py collectstaticto gather all static files intoSTATIC_ROOTfor Nginx or WhiteNoise to serve
Media Files
Files uploaded by users — profile pictures, blog post images, documents.
1MEDIA_URL = 'media/' # URL prefix for user uploads 2MEDIA_ROOT = BASE_DIR / 'media' # Where uploaded files are stored on disk
Critical difference:
- Static = Developer-provided (CSS, JS, logos)
- Media = User-provided (avatars, attachments, uploads)
⚠️ Security: Never serve user-uploaded files directly without validation. Malicious users can upload executable files disguised as images.
⚙️ 12. Django REST Framework Configuration
1REST_FRAMEWORK = { 2 'DEFAULT_AUTHENTICATION_CLASSES': [ 3 'rest_framework.authentication.TokenAuthentication', 4 'rest_framework.authentication.SessionAuthentication', 5 ], 6 'DEFAULT_PERMISSION_CLASSES': [ 7 'rest_framework.permissions.IsAuthenticatedOrReadOnly', 8 ], 9 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 10 'PAGE_SIZE': 20, 11 'DEFAULT_THROTTLE_CLASSES': [ 12 'rest_framework.throttling.AnonRateThrottle', 13 'rest_framework.throttling.UserRateThrottle' 14 ], 15 'DEFAULT_THROTTLE_RATES': { 16 'anon': '100/day', # Anonymous users: 100 requests/day 17 'user': '1000/day' # Logged-in users: 1000 requests/day 18 } 19}
REST Framework Settings Explained
| Setting | Purpose |
|---|---|
DEFAULT_AUTHENTICATION_CLASSES | How users prove their identity to the API |
DEFAULT_PERMISSION_CLASSES | Default access rules (read-only for guests) |
DEFAULT_PAGINATION_CLASS | Automatically paginates large result sets |
PAGE_SIZE | Number of items per page |
DEFAULT_THROTTLE_CLASSES | Rate limiting to prevent API abuse |
DEFAULT_THROTTLE_RATES | Request limits per user type |
Authentication classes:
SessionAuthentication— Uses Django's session cookies (for browser-based API usage)TokenAuthentication— Uses API tokens (for mobile apps and SPAs)JWTAuthentication— Modern token standard (covered in Module 11)
🌐 13. CORS Configuration
1CORS_ALLOWED_ORIGINS = [ 2 "http://localhost:3000", # React development server 3 "http://127.0.0.1:5500", # Live Server (VS Code extension) 4]
What is CORS?
Cross-Origin Resource Sharing (CORS) is a browser security feature. If your Django API runs on http://localhost:8000 but your React frontend runs on http://localhost:3000, the browser blocks the request unless Django explicitly allows it.
Without CORS: Your frontend gets this error:
Access to fetch at 'http://localhost:8000/api/posts/' from origin
'http://localhost:3000' has been blocked by CORS policy.
With CORS: Django adds special headers telling the browser: "It's safe, let the request through."
⚠️ Production: Never use
CORS_ALLOW_ALL_ORIGINS = Truein production. Always whitelist specific domains.
👤 14. Custom User Model
1AUTH_USER_MODEL = 'users.CustomUser'
Why This Matters
By default, Django uses django.contrib.auth.models.User. However, this model uses username as the primary identifier and has limited fields.
In modern apps, you typically want:
- Email-based login instead of username
- Extra fields like
phone,role,is_verified
Telling Django AUTH_USER_MODEL = 'users.CustomUser' means: "Use my custom user model instead of the default one."
🚨 Critical: You must set this before running your first migration. Changing it later requires rebuilding your entire database.
We will build this custom user model in Module 5.
🛡️ 15. Security Checklist for Production
Add these settings when deploying:
1# Security Headers 2SECURE_SSL_REDIRECT = True # Redirect all HTTP to HTTPS 3SESSION_COOKIE_SECURE = True # Only send cookies over HTTPS 4CSRF_COOKIE_SECURE = True # Only send CSRF token over HTTPS 5SECURE_BROWSER_XSS_FILTER = True # Enable browser XSS filtering 6SECURE_CONTENT_TYPE_NOSNIFF = True # Prevent MIME-type sniffing 7X_FRAME_OPTIONS = 'DENY' # Prevent clickjacking 8 9# HSTS (HTTP Strict Transport Security) 10SECURE_HSTS_SECONDS = 31536000 # 1 year 11SECURE_HSTS_INCLUDE_SUBDOMAINS = True 12SECURE_HSTS_PRELOAD = True
🧪 16. Practice Task — Secure Your Project
Complete these steps to apply everything you learned:
Task Checklist
- 1. Install
python-decouple:pip install python-decouple - 2. Create a
.envfile in your project root with these variables:DJANGO_SECRET_KEY=your-unique-secret-key-here DJANGO_DEBUG=True DB_NAME=mydb DB_USER=postgres DB_PASSWORD=yourpassword DB_HOST=localhost DB_PORT=5432 - 3. Update
settings.pyto useconfig()forSECRET_KEY,DEBUG, and database credentials - 4. Add
.envto.gitignore - 5. Install PostgreSQL adapter:
pip install psycopg2-binary - 6. Update
DATABASESto use PostgreSQL configuration - 7. Ensure
INSTALLED_APPSincludesrest_frameworkand your three apps - 8. Add
corsheaderstoINSTALLED_APPSandCorsMiddlewareat the top ofMIDDLEWARE - 9. Add
AUTH_USER_MODEL = 'users.CustomUser'at the bottom - 10. Run
python manage.py check— confirm
Verification Commands
1# Check for system issues 2python manage.py check 3 4# Check deployment readiness (will warn about security settings) 5python manage.py check --deploy 6 7# View your settings (helpful for debugging) 8python manage.py diffsettings
❌ Common Mistakes & How to Fix Them
| Mistake | Error / Symptom | Solution |
|---|---|---|
SECRET_KEY in code | Security vulnerability, exposed on GitHub | Move to .env file immediately |
DEBUG = True in production | Detailed error pages leak server info | Set DEBUG = False, use config() |
| Wrong middleware order | Session not found or auth failures | SessionMiddleware before AuthenticationMiddleware |
Forgot corsheaders in INSTALLED_APPS | CorsMiddleware not found error | Add 'corsheaders' to apps list |
AUTH_USER_MODEL set after migration | IntegrityError or migration conflicts | Delete database and migrations, start fresh |
ALLOWED_HOSTS empty in production | 400 Bad Request on every page | Add your domain to the list |
psycopg2 not installed | ModuleNotFoundError when using PostgreSQL | Run |
✅ Module 2 Summary
| Concept | Key Takeaway |
|---|---|
BASE_DIR | Root project path using pathlib |
SECRET_KEY | Move to environment variables immediately |
DEBUG | True for dev, False for production |
ALLOWED_HOSTS | Domain whitelist for security |
INSTALLED_APPS | Register Django built-ins, third-party, then your apps |
MIDDLEWARE | Request/response pipeline — order matters |
DATABASES | SQLite for learning, PostgreSQL for production |
AUTH_PASSWORD_VALIDATORS | Enforce strong passwords |
STATIC_URL / MEDIA_URL | Static = code assets, Media = user uploads |
REST_FRAMEWORK | Global API behavior settings |
CORS_ALLOWED_ORIGINS | Whitelist frontend domains |
AUTH_USER_MODEL | Tell Django to use your custom user model |
🚪 What's Next?
In Module 3, we dive into models.py — the heart of your database design. You'll learn to create tables, define relationships (One-to-Many, Many-to-Many, One-to-One), and master Django's ORM (Object-Relational Mapper) to interact with your database using Python instead of SQL.
Before proceeding, make sure:
- Your
.envfile is created and.gitignoreignores it settings.pyusesconfig()for sensitive values- PostgreSQL is configured (or you understand how to switch later)
python manage.py checkreturns zero issues- You understand the difference between Static and Media files
Your Django project is now configured like a professional. Ready to build some models? 🗄️🚀