[Django Project Setup for Production] (Part — 2) The “settings” package

Rishabhojha
4 min readAug 14, 2022
Photo by Cesar Carlevarino Aragon on Unsplash

Introduction

This is the 2nd blog in the series “Django Project Setup for Production”. In this blog we will go over the “settings” package of the “DjangoProduction” project; you can clone this project from GitHub repository:

If the above clickable card does not work please use repository link: https://github.com/PythonBitsYT/DjangoProduction

If you would like to setup a new Django Production ready project from scratch, or want to learn more about the “DjangoProduction” project design please refer the first article in this series — [Django Project Setup for Production] (Part — 1) New Project Setup:

https://medium.com/@rishabhojha11/c656f0aff6cd

Multiple requirements file

djangoprod
├── requirements_dev.txt
├── requirements_prod.txt
└── requirements_test.txt
  • We can have different versions of requirements installed in different environments
  • This feature comes in handy while building large applications

Having multiple settings files (Existing Approach)

In Django we can have multiple settings file, and can choose which one to use while running the application using command:

python manage.py runserver --settings=basic_app.settings

If the above command looks familiar to you then you are most probably aware about the code duplicity and maintenance issues if causes for the development teams. It is a common practice to have different settings files for different environments, example:

basic_app
├── __init__.py
├── asgi.py
├── settings_dev.py
├── settings_uat.py
├── settings_prod.py
├── urls.py
└── wsgi.py

Having multiple settings file goes against the DRY (Do not Repeat Yourself) principle of clean and maintainable code. As, you have to copy the project configurations, database configuration, log configurations, app configurations, etc. in all the settings files. This also leaves room for bugs when you miss copying appropriate configurations to the settings file of the higher environment while deployment.

Making “settings” a package (Suggested Approach)

A better way to manage the configurations for multiple environments without breaking the DRY principle is to configure settings at run time. To achieve this we have setup settings as a package in “DjangoProduction” project with multiple files in it, each dealing with a specific feature of settings configuration:

settings
├── __init__.py
├── core_dev.py
├── core_prod.py
├── core_uat.py
├── django.py
├── env_vars.py
└── setup.py

Let us go over each file under settings package in brief:

setup.py

from pathlib import Path
import environ
import sys
import os
# Setup paths for the project
ROOT_DIR = Path(__file__).resolve().parent.parent.parent.parent.parent
BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent
sys.path.append(BASE_DIR)
# Setup ENV for the project
ENV = environ.Env(
# set casting, default value
DEBUG=(bool, False)
)
# Take environment variables from .env file
environ.Env.read_env(env_file=os.path.join(ROOT_DIR, '.env')) # reading .env file
  • This file consists of the root configurations used all across the settings package
  • We set the “ROOT_DIR” and “BASE_DIR” paths in this file
  • The ENV variable is initialised in this file to read the “.env” file from ROOT_DIR

env_vars.py

  • This file consists of all the settings env variables that will be used in the project
  • We generally initialise the AWS, Database, and Cache related variables in this file

django.py

  • This file consists of the configurations related to admin, timezone, applications, password validators, and middleware that are common across project environments
  • Some settings here, for example, INSTALLED_APPS can be appended/overridden in core_<env>.py file as and when required

core_dev/core_uat/core_prod.py

  • These are the environment related files where the project environment specific configurations such as — Logging, Database, and Cache configurations live.
  • Which file to execute is based on the environment and is selected at runtime using environment variables. (more details on this below under __init__.py configuration details)

__init__.py

from .setup import ENVif ENV("ENVIRONMENT")=="PROD":
from .core_prod import *
elif ENV("ENVIRONMENT")=="UAT":
from .core_uat import *
else:
from .core_dev import *
  • This file acts as a driver code in deciding which “core_<env>” configurations to initialise based on the “ENVIRONMENT” variable defined in “.env” file in ROOT_DIR

By creating settings as a package rather than file, and using “django-environ” package-distribution we can select the appropriate settings (configurations) at run time with the help of .env file.

.env

# Django Related
DEBUG="Value for Django Debug"
ENVIRONMENT="Execution environment"
LOG_DIR_PATH="Path to log file folder/package"
# DB Related
DATABASE_URL="DB connection URL"
DATABASE_READ_REPLICA_1_URL="DB read replica connection URL"
# AWS Related
AWS_ACCESS_KEY_ID="AWS user access key ID"
AWS_SECRET_ACCESS_KEY="AWS user access secret key"
AWS_STORAGE_BUCKET_NAME="AWS storage bucket name"
AWS_STORAGE_BUCKET_REGION="AWS storage bucket region"
AWS_STATIC_STORAGE_BUCKET_NAME="AWS static storage bucket name"
  • Above is an example .env file.
  • You can plug in the values for all the keys above and save it under ROOT_DIR of your project (which is as per our settings the parent directory of the project)

With the above setup you will be able to run “DjangoProduction” project for any environment using basic runserver command:

python manage.py runserver 

In next part of this series we will go over “config” package under which the “settings” package reside and the “common” application with its advantages and dis-advantages.

[Django Project Setup for Production] (Part — 1) New Project Setup: https://medium.com/@rishabhojha11/c656f0aff6cd

[Django Project Setup for Production] (Part — 3) “config” package and “common” app: https://medium.com/@rishabhojha11/89e451d90476

--

--

Rishabhojha

Senior Python Developer, Django Enthusiast, Researcher