Using multiple settings.py in your Django project
Posted on Mon 07 October 2019 in Python • 4 min read
After going through "Django for professionals" and building the project in there, I started to build a Django project template based on what I learned from the book. The book goes through a development version of the project, and a production version, and it also makes use of Docker to run the web server and PostgreSQL server.
I realized it would be very useful to have one settings file for development and one for production. For example, in development I might not want to send emails via a SMTP service but just print the mail to the console, and in production there would be security settings which would be impractical in development.
Another thing I wanted to accomplish was to hide away certain variable (like SECRET_KEY and any passwords) so they wouldn't be pushed to GitHub.
In this article I'll explain how I set up all those things in my Django project template. You can also find the full code in my Django template repo. It might be useful to use that as a reference when going though this article.
Project structure
After creating a Django project there is one settings.py
in the project folder. To split up the settings I start by creating a new folder (called settings) under the projects folder and add an __init__.py
file within it. I move the original settings.py
file into the settings folder and rename it to base.py
. This file will hold the settings that are common for both development and production.
I then create a development.py
and a production.py
for the settings that will differ between the environments.
The setting files
At the top of each of these files I import the settings from base.py
.
from .base import *
Then it is just a matter of moving the settings from base.py
that would differ from the different environments. For example this is how my development.py
looks like:
from .base import *
DEBUG = True
INSTALLED_APPS += [
'debug_toolbar',
]
MIDDLEWARE += [
'debug_toolbar.middleware.DebugToolbarMiddleware',
]
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEBUG_TOOLBAR_CONFIG = {
'JQUERY_URL': '',
}
# Django-debug-toolbar
import socket
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
INTERNAL_IPS = [ip[:-1] + "1" for ip in ips]
Running the server
To start the Django server with a specific settings file run the following command:
python manage.py runserver --settings=project.settings.development
Lift out "secret" variables from the settings
Certain variables in the setting files is not something you'd want to publish on GitHub (or similar). For example the SECRET_KEY or any password to other services.
What I did here was to create an .env
in the root of the project to put those variables into. For example:
SENDGRID_USERNAME=myusername
SENDGRID_PASSWORD=mypassword
SECRET_KEY=mysecretdjangokey
In base.py
I then read the parameters from the .env
file with os.environ.get()
.
SECRET_KEY = os.environ.get('SECRET_KEY')
If you're not using Docker to develop your Django app, this might be almost all you need.
Update manage.py
One more thing is needed though. I found this out the hard way. Everything worked fine for me with the different setting files until I wanted to do a migration. Then I got a strange error that the SECRET_KEY value was empty. After lots of troubleshooting I found out that the manage.py
still tried to read from the original settings.py
.
So this line in manage.py
:
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
Needs to be updated to:
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings.base')
Docker compose
Since the manage.py runserver
command will have a different parameter depending on if you want to run it with development settings or with production settings, I also split up the docker-compose.yml
into two different files. The only difference between these two at the moment is the parameters of the runserver
command.
You can run a specific docker compose file like this:
docker-compose -f docker-compose-dev.yml up -d
Now this can be pretty tiresome to type every time, especially if you need to do a exec web
or look at the logs in your container etc. So on top of all this, I put common commands that I use with docker compose into a Makefile
.
(Note also that I read in the .env
file withing my docker compose files)
Makefile
In the Makefile I construct different common commands. Like:
- Looking at the logs in the container
- Shut down the container
- Execute a command within the container
Here is an excerpt of my Makefile:
dev:
@docker-compose \
-f docker-compose-dev.yml \
down && \
docker-compose \
-f docker-compose-dev.yml \
up -d
dev_build:
@docker-compose \
-f docker-compose-dev.yml \
down && \
docker-compose \
-f docker-compose-dev.yml \
up -d --build
dev_logs:
@docker-compose -f docker-compose-dev.yml logs
dev_down:
@docker-compose -f docker-compose-dev.yml down
dev_web_exec:
@docker-compose -f docker-compose-dev.yml exec web $(cmd)
So to spin up my development environment I just run:
make dev
To look at the logs while the container is running:
make dev_logs
Conclusion
This is one way to use multiple setting files in you Django project. I also showed you how using these in a Docker scenario can be made easier with different docker-compose.yml and a Makefile to make the commands needed to be much shorter.
Let me know on Twitter if I can improve this article, or if you have other resources to help out with understanding this topic.