Django separate Settings files

Created: 27.06.2022 | Last edited: 19.07.2022

Motivation

You might want to have separate settings files in Django.

Well, even just because something like DEBUG=True exists and it's quite inconvienient to go on your deployment server and change it to False every time you pull new changes to it should be reason enough. This also usually forces you to stash changes before pulling as your local settings file on the deployment server doesn't match the pulled version of your code on, lets say GitHub for example, when using Git.

While there are several reasons and several ways to achieve different settings files for different environments, following solution is my favorite one as it is very simple, straight forward, and effective.

Creating a settings folder

First off, we will create a folder called settings right where our current settings.py file lives.

Next, we will move the existing settings.py into our new folder. Note: if your IDE asks if it should refactor: DON'T hit "yes"! We will convert the folder to a package providing an __init__.py file, which means existing references will still work as usual which is part of why I like this solution.

Organizing new settings files

Now we can rename our settings file to "_base.py". We will start the name with an underscore to mark it as "interal", not to be used directly. It has not much of a functional use but is a useful convention like starting method names with underscores to mark them private. While the latter has impact on how import statements work, there isn't really a hard "private" and "public" in Python like there is in Java for example.

We will also create three more empty files: __init__.py, dev.py, and prod.py.

_base.py, as labelled internal by convention, is a settings file we will never use directly. Here we will store basic settings that we need in all our settings configurations, no matter if we run our Django webapp in development, production, or somewhere else. An example could be settings for languages or certain plugins or our secret key (even though we should use environment variables like shown in my other post instead of storing cleartext right in the code but that's a different story). As a first step, we should also delete DEBUG=True from our _base.py file and cut the configuration for the basic sqlite database.

Populating different settings

Next we will paste the database configuration in our dev.py file, import setting from _base.py, and set DEBUG to True:

from ._base import *

DEBUG = True

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

For our prod.py file we can set an Database used in production accordingly, import every base setting, and set DEBUG to false:

from ._base import *

DEBUG = False

# Production Database Setup

DATABASES = { ... }

Choosing the right settings

Quick and dirty (for private projects)

Next we will take care about the __init__.py file. Now the cleanest solution would be to tell django it should fetch the path to the right settings from environment variables and provide a script to set this variable, and probably others, for developers, as well as to set an variable accordingly on our production server. However, for simple private projects or prototpyping where I don't care about other developers, as there mostly aren't any, or a properly configured server I prefer a simpler solution: in the __init__.py file we will import everything from "prod.py":

 

Now every line of code that tries to read our old path to the "settings.py" file which doesn't exist anymore will instead find our new "settings"-folder (package) and draw from the __init__.py file. So we will push our code to the production server with the production settings set.

Next, we'll change our GitIgnore to also Ignore our __init__.py file:

# local test db
./db.sqlite3

# choice of settings file
./portfoliov3/settings/__init__.py

And lastly we can change our __init__.py file locally to use "dev.py" again for our development environment:

from .dev import *

This way it is ensured our production server will keep using the "prod.py" settings as the access to this file over the package initialization can't be changed anymore. Small caviat: if you would clone the project again or another developer clones it, the initialization file has to be set to use "dev.py" manually over and over again.

However, this problem only occurs when you are using a single branch to push your code onto (which you shouldn't!!!!!). If you have at least a production branch that gets deployed to your production server, and a development branch for your dev code you could't set the init's configuration accordingly different for both branches.

Summary: this is a simple and straight forward way to keep different settings without any refactoring. However, for real world use you should back down to using environment variables or any other scalable and more collaboration friendly system.

Using DJANGO_SETTINGS_MODULE (the django way)

Now let's gear towards the "real" solution. So there is this thing called "DJANGO_SETTINGS_MODULE". Basically you can set a simple environment variable using a script like this:

export DJANGO_SETTINGS_MODULE=mysite.settings.dev

export CUSTOM_ENVIRON_CHECK='Environment Variables set successfully.'
echo "$CUSTOM_ENVIRON_CHECK"

(I like to include a quick and simple feedback in the console to see if everything was executed)

After this you can simply run your server and it should pick the right settings file. With a shell script for production and another one for development, both set up to run automatically before the server starts in their environments, you should be good to go.