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.

Django project structure image

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.

Resources