For experienced devs, this is a no-brainer but if you’re a new Django developer like me and you have different settings for prod, staging, and local development then you might be thinking about how to have Django Settings for Multiple Environments. Like all of the posts in this blog, this is written primarily to save my time in the future, but at the same time, if it helps someone else, that’s great!!!
If you google the same question, you’ll come across multiple approaches that are very different. If I had to massively generalize them, then I’d put them in the following categories:
- Having separate settings files named
settings_local.py
,settings_local.py
, etc. and then pass the settings with--settings
flag inmanage.py
call - Having a module
settings_split
or similar name and in that modulebase_settings.py
,prod_settings.py
etc. whilesettings.py
has the logic for correctly importing the right settings based on the environment variable - Having a module named
settings
(Django by default looks for this), the module has thebase_settings.py
,prod_settings.py
etc. and the__init__.py
has the logic for choosing the right settings
To split Django settings for different environments, I like the last approach best. Because the files are located together in a module and the module name is the exact one that Django looks for by default. It also doesn’t involve any extra steps while using the manage.py
commands or modifying the manage.py
file. All these settings are in my version control, so whenever I start working on a different machine, I just have to edit the .envrc.sample
file and put appropriate environment variables, and it’s all up and running. The 2nd approach is also good, but I really don’t think the settings.py
file should just be hanging there in the project and only have logic while the actual settings are somewhere else.
I follow the following best practices while working on any Django project:
- Have settings that are unique to the machine running the project like Database config, secret keys, API keys, etc on a
.envrc
file (on my dev machine I use tools like https://direnv.net/ to load in the environment variables from the.envrc
file). I make sure NOT TO CHECK THIS FILE OUT WITH GIT and would usually provide a.envrc.sample
checked into git. - Create a new directory in my Django project module named
settings
and copy the contents ofsettings.py
insettings/base.py
then delete thesettings.py
file. - Create
settings/__init__.py
file so thatsettings
directory is recognized as a module and has the following logic for loading in the right settings:
import os
from django.core.exceptions import ImproperlyConfigured
ENVS = ["DEV", "PROD", "STAGING"]
env = os.getenv("ENV")
if env not in ENVS:
error_message = "The currnet 'ENV' is {env} but must be one of {ENVS}"
raise ImproperlyConfigured(error_message)
# match is a new keyword in Python 3.10, if your python version is less than 3.10, re-write this block with if-else
match env:
case "DEV":
from .dev import *
case "PROD":
from .prod import *
case "STAGING":
from .staging import *
- Create different settings files for the different environments you want to define inside the
settings
directory, e.g.dev.py
,prod.py
etc. Example of a sampledev.py
andprod.py
:
# Example dev.py
from .base import *
DEBUG = True
ALLOWED_HOSTS = ["localhost", "127.0.0.1"]
INSTALLED_APPS += ["debug_toolbar"] # You can add extra apps on the individual environment.
# Example prod.py
import os
from .base import *
DEBUG = False
ALLOWED_HOSTS = ["iammahir.com"]
SECRET_KEY = os.getenv("SECRET_KEY")
- Don’t forget to set
ENV
environment variable for this all to work, you can either addexport ENV="DEV"
in your.envrc
or.bashrc
or.zshrc
depending on the setup.
We’re all set! Everything should work, if you have any questions or trouble making this work or have any opinion about this approach, feel free to look at my very much work-in-progress project or reach out to me at [email protected] or@m4hi2
on all platforms 🙂