settings.py

What is it for?

The settings.py file in your project_name folder holds all the settings for your project, including debug mode, database configuration, and template locations and should be kept up to date to ensure that your app works well. Here I have outlined the sections of the file along with some common edits to the settings.py file specifically to add functionality to your webapp as a repository of information. More information on how the app as a whole needs to be updated for each functionality can be found in the following pages, often linked from their section here. For a broader idea of what is happening, check out the other pages in this Django section, but to troubleshoot issues, these updates to your settings.py file should be one of your first stops.

Each section of the settings.py file has a handy link to the Django documentation to help you customise your webapp.

Layout of the settings.py File

Imports

The top of the file has only one import from the inbuilt python library pathlib, that Django uses in order to locate files and directories without the need for using strings for the paths. This provides various functionalities to the user including checking the existence of a path and checking if it is a path to a file or directory. This section will expand as you find the need for more libraries as your project grows but it is best practice to keep all the imports at the top of the file like this.

from pathlib import Path

Base Directory Definition

This definition of the BASE_DIR variable should not be altered.

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
  • __file__ refers to the current file; settings.py
  • .resolve() gets the absolute path of the file
  • .parent moves one level up the root directory, so .parent.parent moves two levels up
  • This final location acts as the base directory for your project

Security Key

This is a very important variable for your project. Django uses this secret key as part of the algorithm to hash user passwords, ensuring that user accounts remain safe, along with being used to create CSRF tokens and data validation tokens. If this is leaked, it would allow attackers to impersonate users, decrypt session data, and forge authentication tokens or bypass CSRF protection or generate valid reset links.

If you have already pushed your project to GitHub, this value will be available to anyone looking through your commit history, but that's not the end of the world as you can generate a new one. The Django Secret Key Generator can be used to create secret keys specifically for Django, matching their level of security.

It is best practice for reasons mentioned above to move this to your env.py file (or generate a new one and move that).

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-random-letters-and-numbers'

Debug

You will find that when you create your project, the DEBUG variable is set to True. This is indispensable throughout development, letting you, well, debug, any errors that may arise. However, for best practice, this definition should be moved to your env.py file, allowing you have a debug mode on when making alterations locally but having it turned off in the final deployed website.

Having DEBUG=True on a deployed site can cause the following issues:

  • Security Risks
    • The debug mode will display detailed error messages that help you when you build a website. However the provided variables, database credentials, and file paths can be used by attackers to exploit vulnerabilities.
  • Performance Issues
    • It's very handy to get detailed error reports when something goes wrong to help us pinpoint where the issue is and apply a fix. However, the extra logging and error reporting consumes system resources and can slow down your site which is not an ideal situation for your users.

Allowed Hosts

The ALLOWED_HOSTS variable is an added security measure that defines which host/domain names that your Django application can serve, thereby preventing HTTP Host header attacks. This is a Python list of strings, so we can now add either 'localhost' or '127.0.0.1' to this variable (or both) to cover our development and add your deployed domain when the site goes live.

ALLOWED_HOSTS = ['localhost', '127.0.0.1']

Installed Apps

The INSTALLED_APPS section defines the list of applications that are active in your Django project and include built-in Django apps, third part apps or custom apps. Apps will be discussed more in their own section here but for now it is best to know that this is where you add both downloaded apps and your own created apps in order for them to work.

Adding an app to this list makes them visible to Django and help it assess changes when migrating and automatically integrate app features.

INSTALLED_APPS = [
    'django.contrib.admin',          # Django's built-in admin panel  
    'django.contrib.auth',           # Authentication system  
    'django.contrib.contenttypes',   # Framework for handling content types  
    'django.contrib.sessions',       # Manages user sessions  
    'django.contrib.messages',       # Enables messaging framework  
    'django.contrib.staticfiles',    # Handles static file management  
]

Middleware

In Django, the MIDDLEWARE setting is a list of middleware components that sit between the request and response phases of a web request. They act as a series of hooks that process incoming requests and outgoing responses. Each middleware can:

  • Process requests before views are called
  • Process responses before they're returned to the browser
  • Handle exceptions, redirects, authentication, security checks, etc.

Here are the default ones and their uses:

MIDDLEWARE = [
	# Adds security headers (e.g., HSTS, SSL redirects)
    'django.middleware.security.SecurityMiddleware',
	# Enables session support using cookies
    'django.contrib.sessions.middleware.SessionMiddleware',
	# Adds standard behaviors (e.g., URL normalization, APPEND_SLASH)
    'django.middleware.common.CommonMiddleware',
	# Protects against CSRF attacks by enforcing CSRF tokens
    'django.middleware.csrf.CsrfViewMiddleware',
	# Associates users with requests using sessions
    'django.contrib.auth.middleware.AuthenticationMiddleware',
	# Enables temporary messages via Django's messaging framework
    'django.contrib.messages.middleware.MessageMiddleware',
	# Prevents the site from being embedded in iframes (defends against clickjacking)
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF

This setting tells Django where to find the URL configuration for your project. It will link to the urls.py file in your project name folder with dot notation. E.g.

ROOT_URLCONF ='project_name.urls'

Will tell django to import project_name/urls.py

So when a request comes in, Django looks at the ROOT_URLCONF to find the correct urls.py file, loads the list of urlpatterns inside that file and matches the incoming URL against the patterns to find the right view to run.

Templates

The TEMPLATES variable defines how Django handles rendering the HTML templates by defining the path to the templates, which template engine to use and other settings for processing the templates.

Here is an outline of the keys In the TEMPLATES dictionary:

  • BACKEND:
    • Defines which template engine should use, with the default being django.template.backends.django.DjangoTemplates which is the built-in template engine.
    • You can add third-part engines here such as Jinja2 with by replacing it with django.template.backends.jinja2.Jinja2
  • DIRS:
    • This is a list of directories that Django looks in for templates. By default it is empty. When adding directories here, the general syntax is
    • 'DIRS': [os.path.join(BASE_DIR, 'templates')]
    • For child folders you can specify them like:
      'DIRS': [os.path.join(BASE_DIR, 'templates', 'allauth')] which will look in the allauth folder inside the templates folder in the root directory.
  • APP_DIRS:
    • If True, Django will also look for templates inside each installed app's templates folder.
    • If False, Django will only use the directories specified in DIRS
  • OPTIONS:
    • The most common setting here is the context processors that automatically add extra data to every template, so you can define your own and include them in this list

WSGI_APPLICATION

This setting in settings.py tells Django where to find the WSGI application object that serves your project when it's deployed. It will be in the form of:

WSGI_APPLICATION = 'project_name.wsgi.application'

Django will look in your project folder, find the wsgi.py file inside that folder and call the Python object defined inside wsgi.py.

The WSGI is a standard interface between your web server (like Gunicorn, uWSGI, or mod_wsgi), and your Python web app (in this case, your Django project). When the server receives a request, it passes it to the application object defined here. Django processes the request and returns a response through this same object.

Databases

Here we can define our databases, for local development it would be the SQLite3 RDBMS but if you host your database on an external server such as PostgreSQL, you will need to define it here when you deploy your app.

AUTH_PASSWORD_VALIDATORS

This setting defines the password validation rules Django uses when users create or change their passwords. It is a list of rules Django uses to enforce strong, secure passwords in order to protect your users from weak, common, or easily guessed passwords during registration and password changes.

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': (
            'django.contrib.auth.password_validation.'
	# Prevents passwords that are too similar to the user's personal info (e.g., username, email, first name).
            'UserAttributeSimilarityValidator'
        ),
    },
    {
        'NAME': (
            'django.contrib.auth.password_validation.'
# Ensures the password is at least a minimum number of characters (default is 8, but you can customize this).
            'MinimumLengthValidator'
        ),
    },
    {
        'NAME': (
            'django.contrib.auth.password_validation.'
	# Blocks passwords that are too common or easily guessable (like "123456", "password", etc.).
            'CommonPasswordValidator'
        ),
    },
    {
        'NAME': (
            'django.contrib.auth.password_validation'
	# Rejects passwords made up of only numbers (e.g., "12345678").
            'NumericPasswordValidator'
        ),
    },
]

Each validator can also be configured with extra options. For example you can raise the minimum password length to 12 characters.

{
    'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    'OPTIONS': {
        'min_length': 12,
    }
}

The Django Documentation shows all other ways to customise these.

Internationalization

These settings control how Django handles language, time, and localisation.

# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/

# Sets default language for your site
LANGUAGE_CODE = 'en-us'
# Se the default time zone for your project
TIME_ZONE = 'UTC'
# USE_INTERNATIONALIZATION. If True, enables Django's translation system
USE_I18N = True
# Enables Django's time zone support
USE_TZ = True

You can set USE_I18N to False if your site is only ever in one language and you want to skip translation overhead.

Static Files

The pathway to your static files such as images and custom JavaScript code. We will update and expand this section when adding our static files.

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/

STATIC_URL = 'static/'

DEFAULT_AUTO_FIELD

The DEFAULT_AUTO_FIELD controls the type of primary key field that Django automatically adds to your models when you don't explicitly define one. BigAutoField is a 64-bit integer that auto-increments. It's used as the id field (the primary key) by default when you define a model.

# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

How Tos

Linking Your env.py File

Clearly any sensitive information such as secret keys should not be kept in your online repository, especially if it is made public. Therefore, linking your settings.py file to your env.py file is imperative to keep good practices and a high level of security to your site. Information on setting up your env.py file can be found here.

Update the settings.py file imports to include:

We used this built in python module to define the environmental variables in our env.py file and will need it in our settings.py file to retrieve them. To keep to good practices, add this below the other imports at the top of the settings.py file.

Then under the other imports make a check for the env.py file and if found import it. The if statement is used so that when we deploy, Django will obviously not have access to our local file which would result in an error if we did not include this check for it first.

Then update your SECURITY_KEY, getting the value defined in your env.py file.

This makes it look for the value of SECRET_KEY in the env.py file. The second instance of SECRET_KEY in this line refers to the name you have given to the variable in your env.py file because the os.getenv function it will look through your env.py file for the variable that matches whatever is used as the argument. For example if you named it something else like PASSCODE, your updated settings.py SECRET_KEY will look like:

Update your debug DEBUG.

This check allows the Django to launch in debug mode when working locally with access to your env.py file providing you a better time of debugging and then automatically switch to DEBUG = False when deployed, providing the user with the best user experience.

Adding Apps

When you have created an app, add it to the list of INSTALLED_APPs like so:

INSTALLED_APPS = [
    'django.contrib.admin',  # message
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # File storage
    'whitenoise',
    # Custom apps
    'core',
    'homepage',
    'intro_to_full_stack',
    'HTML',
    'CSS',
    'javascript_app',
    'python_app',
    'backend',
    'flask_app',
    'analytics',
]

AllAuth

Django allauth requires significant changes/additions to your settings.py file depending on the functionality that you want to introduce.

Context Processors

Context Processors are used to inject additional variables into every template context and can be registered by appending it to the context_processors key of the OPTIONS dictionary of the TEMPLATES dictionary with the syntax of app_name.file_name.function_name. Such as here where I add the media_url function to add the variable MEDIA_URL to each template by appending 'core.context_processor.media_url'.

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates'),],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'core.context_processor.media_url'
            ],
        },
    },
]

Enable Messages

These are included by default so no changes should be required here.

INSTALLED_APPS = [
    'django.contrib.messages',
    # other apps...
]

MIDDLEWARE = [
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    # other middleware...
]

You can overwrite the default values like so:

from django.contrib.messages import constants as message_constants

MESSAGE_TAGS = {
    message_constants.DEBUG: 'debug',
    message_constants.INFO: 'info',
    message_constants.SUCCESS: 'success',
    message_constants.WARNING: 'warning',
    message_constants.ERROR: 'error',
}

Or even make custom messages:

message_constants.SUCCESS: 'green-toast'

Static and Media Files

To ensure that Django can find your CSS, JS, and media files, you will need to make sure that the paths are defined in settings.py correctly. This can be done under the Static files section.

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/

STATIC_URL = 'static/'
STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') 
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Cloudinary

Add required cloudinary imports.

import cloudinary
import cloudinary.uploader
import cloudinary.api

Add cloudinary to installed apps.

INSTALLED_APPS  [
    "cloudinary",
    "cloudinary_storage",
]

Add in the Cloudinary variables from your Cloudinary dashboard. I keep these at the top of the Static files section. For security you should define these in your env.py file and access them from there.

CLOUDINARY_NAME = os.getenv('CLOUDINARY_NAME')
CLOUDINARY_API_KEY = os.getenv('CLOUDINARY_API_KEY')
CLOUDINARY_SECRET_KEY = os.getenv('CLOUDINARY_SECRET_KEY')

CLOUDINARY_STORAGE = {
    "CLOUD_NAME": CLOUDINARY_NAME,
    "API_KEY": CLOUDINARY_API_KEY,
    "API_SECRET": CLOUDINARY_SECRET_KEY
}

# Create a DEFAULT_FILE_STORAGE variable
DEFAULT_FILE_STORAGE = "cloudinary_storage.storage.MediaCloudinaryStorage"

Update your MEDIA_URL to have local files in development and externally hosted images during production.

if DEBUG:
    # Local development settings
    MEDIA_URL = '/media/'
    MEDIA_ROOT = BASE_DIR / 'media'
else:
    # Production (Cloudinary)
    MEDIA_URL = f"https://res.cloudinary.com/{CLOUDINARY_NAME}/image/upload/f_auto,q_auto/"
    MEDIA_URL_VIDEOS = f"https://res.cloudinary.com/{CLOUDINARY_NAME}/video/upload/"

Whitenoise

  • Add whitenoise to your INSTALLED_APPS
  • In your static files section add a STATICFILES_STORAGE variable linking to whitenoise.
  • Update your STATIC_ROOT variable to access staticfiles
  • Add whitenoise to MIDDLEWARE
INSTALLED_APPS = [
    # File storage
    'whitenoise',
]

STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') 

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",  # Add this
    ...
]

AllAuth

See all of the changes on the AllAuth installation guide that you will need to make to your settings.py file.

Translation

Add the relevant imports to the top of the file.

# Import gettext_lazy at the top using the shorthand of _
from django.utils.translation import gettext_lazy as _

In the Internationalization section, add a LANGUAGES variable which will be a list of tuples. The tuples will be the language code as a string and the gettext_lazy of the language name. Then update your LANGUAGE_CODE variable to show the default language that you want for your website and add a LOCALE_PATHS to have the pathway of the stored location. We have not created this folder yet but the accepted name is 'locale'.

LANGUAGES = [
    ('en', _('English')),
    ('ka', _('Georgian')),
]

LANGUAGE_CODE = 'en'  

LOCALE_PATHS = [
    BASE_DIR / 'locale',  # Path where translation files will be stored
]

#Make the following True:
USE_I18N = True
USE_L10N = True
USE_TZ = True

Add locale middleware to middleware. It needs to go after sessions (.SessionMiddleware) because it will run in a session but above common (.CommonMiddleware) because common deals with the URL and we will set up the URLs depending on the language.

MIDDLEWARE = [
	…
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',  # Languages
    'django.middleware.common.CommonMiddleware',
	…
]