Dummies Introduction to Django. Part 4 – Settings

We have covered the basics of creating a Django web application. However, before we can go ahead and deploy our application to a website, there’s an elephant in the room we have to acknowledge. Go and take a look at the project/settings.py file. Here are (some) of the settings in there that we need to take care of:

...
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '...'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []
...
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'djangodb',
        'USER': 'djangouser',
        'PASSWORD': 'secret',
        'HOST': 'localhost',
    }
}
...

These are all either settings that need to vary between our development environment and a live environment, or settings that don’t belong in version control. We need to pull these from somewhere outside our application.

So how do we do this? Unfortunately, there does not appear to be any standard way to do this in Django. The Django website offers half a dozen different solutions. I asked a couple of Django developers in my building and they all had different ways to do this too. There is no “right” way to do it: Just a way that works for you.

My chosen method is to store the settings in an external YAML file. This is easier than you think because the settings.py file is just a plain Python file so you can embed whatever code you want in there. (This is probably also why there are so many ways to tackle this problem: You can code whatever solution you can dream of in settings.py)

First off, we need to add a YAML library to our project.

(Django_Project) $ pip install pyyaml

Now we have to create a YAML configuration file for our application to use. This is the core of what I use:

application:
 - secret_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   debug: true
   allowed_hosts:
   database_password: secret

Yaml is quite a powerful and flexible file format, so you could structure the data in numerous ways. But this is the way I choose. Make sure to change the “secret_key” and “database_password” value to whatever is currently in your settings.py file.

I called this file “settings.yaml” and put in in the root of our virtual environment:

.
├── bin
├── include
├── lib
├── main
├── manage.py
├── project
└── settings.yaml

Now we have to tell our Django app to read and use this new file. First off, at the top of the project/settings.py file, add the line “import yaml” just below the existing “import os” line”

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.0/ref/settings/
"""

import os
import yaml   <==== Our new line

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)

Next, at the bottom of the settings.py file, we need to add a chunk of code to read and use settings.yaml. First we need to open the file. But before we can do that, we need to know where to find the file. First, we’ll look for an environment variable called “APPLICATION_CONFIG_FILE”. If it exists, we’ll use it as the full path for our yaml file. If the environment variable is not set, we just assume the file is in the root of our virtual environment. This technique will be very useful later on – trust me!

configuration_file = os.getenv('APPLICATION_CONFIG_FILE')
if configuration_file is None:
  configuration_file = './settings.yaml'

Next we have to load our YAML data into memory. Pyyaml provides several methods for loading yaml files. We’ll start with using the safest one.

yaml_data = yaml.safe_load(open(configuration_file, 'r'))

You can read more about the different loader methods available at Pyyaml’s Github page.

Finally, we can take the parameters from the yaml file and apply them to Django

SECRET_KEY = yaml_data['application'][0]['secret_key']
DEBUG = yaml_data['application'][0]['debug']
DATABASES['default']['PASSWORD'] = yaml_data['application'][0]['database_password']
hosts = yaml_data['application'][0]['allowed_hosts']
if hosts is not None:
  for host in hosts:
    ALLOWED_HOSTS.append(host)

There is no error checking in this code. This is a deliberate decision: If we can’t load any of these settings, there’s no point in carrying on as our application won’t work.

Save all theses files and you should be able to run your Django application as before.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s