When working when configuring a Django project, you will surely deal with some sensitive information, such as passwords and API keys. It is not recommended that you put that information under version control. There are two main ways to store that information: in environment variables and in separate untracked files. In this recipe, we will explore both cases.
Handling sensitive settings
Getting ready
Most of the settings for a project will be shared across all environments and saved in version control. These can be defined directly within the settings files; however, there will be some settings that are specific to the environment of the project instance or that are sensitive and require additional security, such as database or email settings. We will expose these using environment variables.
How to do it...
To read sensitive settings from the environment variables, perform these steps:
- At the beginning of settings/_base.py, define the get_secret() function as follows:
# settings/_base.py
import os
from django.core.exceptions import ImproperlyConfigured
def get_secret(setting):
"""Get the secret variable or return explicit exception."""
try:
return os.environ[setting]
except KeyError:
error_msg = f'Set the {setting} environment variable'
raise ImproperlyConfigured(error_msg)
- Then, whenever you need to define a sensitive value, use the get_secret() function, as shown in the following example:
SECRET_KEY = get_secret('DJANGO_SECRET_KEY')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': get_secret('DATABASE_NAME'),
'USER': get_secret('DATABASE_USER'),
'PASSWORD': get_secret('DATABASE_PASSWORD'),
'HOST': 'db',
'PORT': '5432',
}
}
How it works...
If you run a Django management command without the environment variable set, you will see an error raised with a message, such as Set the DJANGO_SECRET_KEY environment variable.
You can set the environment variables in the PyCharm configuration, remote server configuration consoles, in the env/bin/activate script, .bash_profile, or directly in the Terminal like this:
$ export DJANGO_SECRET_KEY="change-this-to-50-characters-long-random-
string"
$ export DATABASE_NAME="myproject"
$ export DATABASE_USER="myproject"
$ export DATABASE_PASSWORD="change-this-to-database-password"
Note that you should use the get_secret() function for all passwords, API keys, and any other sensitive information that you need in your Django project configuration.
There's more...
Instead of environment variables, you can also use text files with sensitive information that won't be tracked under version control. They can be YAML, INI, CSV, or JSON files, placed somewhere on the hard disk. For example, for a JSON file, you would have the get_secret() function, like this:
# settings/_base.py
import os
import json
with open(os.path.join(os.path.dirname(__file__), 'secrets.json'), 'r')
as f:
secrets = json.loads(f.read())
def get_secret(setting):
"""Get the secret variable or return explicit exception."""
try:
return secrets[setting]
except KeyError:
error_msg = f'Set the {setting} secret variable'
raise ImproperlyConfigured(error_msg)
This reads a secrets.json file from the settings directory and expects it to have at least the following structure:
{
"DATABASE_NAME": "myproject",
"DATABASE_USER": "myproject",
"DATABASE_PASSWORD": "change-this-to-database-password",
"DJANGO_SECRET_KEY": "change-this-to-50-characters-long-random-string"
}
Make sure that the secrets.json file is ignored from the version control, but for convenience, you can create sample_secrets.json with empty values and put it under version control:
{
"DATABASE_NAME": "",
"DATABASE_USER": "",
"DATABASE_PASSWORD": "",
"DJANGO_SECRET_KEY": "change-this-to-50-characters-long-random-string"
}
See also
- The Creating a project file structure recipe
- The Working with Docker containers for Django, Gunicorn, Nginx, and PostgreSQL recipe