Configuring a Django project can seem a little daunting, especially when it comes to managing environment-specific settings. For example, you will want to use different settings when developing locally versus settings you may want to have in a staging or production environment.
If we follow the 12-factor principles, it can make that task a little easier. To streamline this process in Django, we can leverage a powerful library called django-environ. In this article, we'll explore how django-environ can help align your Django project with the 12-factor principles by managing environment variables and separating configuration from code.
The 12-factor principles are a set of best practices for building modern, cloud-native applications. They provide guidelines for structuring and managing applications in a scalable, maintainable, and portable manner. One of the key principles is the separation of configuration from code. You can always visit the 12factor website for more information.
Before we dive into the details, let's make sure we have django-environ installed in our Django project. You can install it using pip:
pip install django-environ
Once installed, we're ready to begin the process to enhance our Django project with better configuration management.
Instead of hardcoding configuration values in your Django settings file, django-environ allows you to store them in environment variables. This not only adheres to the 12-factor principle of separating configuration from code but also makes it easier to manage different configurations for different environments.
To get started, create a .env
file in your project's root directory. Add your environment-specific configuration variables in the file. For example:
# your local .env file
DEBUG=True
SECRET_KEY=your_secret_key
DATABASE_URL=postgres://username:password@localhost/db_name
Now that we have our configuration stored in environment variables, let's load them into our Django settings. Open your settings file (e.g., settings.py
) and import env from django-environ:
import environ import os env = environ.Env( # set casting, default value DEBUG=(bool, False) ) # Set the project base directory BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Take environment variables from .env file environ.Env.read_env(os.path.join(BASE_DIR, '.env')) # False if not in os.environ because of casting above DEBUG = env('DEBUG') # Raises Django's ImproperlyConfigured # exception if SECRET_KEY not in os.environ SECRET_KEY = env('SECRET_KEY') # Parse database connection url strings # like psql://user:pass@127.0.0.1:8458/db DATABASES = { # read os.environ['DATABASE_URL'] and raises # ImproperlyConfigured exception if not found # # The db() method is an alias for db_url(). 'default': env.db(), }
In the code above, we import Env from django-environ and create an instance called env. We cast DEBUG
to a bool value that defaults to False
. This casting can allow you to set sane defaults for important variables if you desire (such as setting DEBUG
to False
by default). We then read the env file and use env.bool()
to read the DEBUG
variable as a boolean value, env.str()
to read the SECRET_KEY
variable as a string, and env.db()
to parse the DATABASE_URL
variable as a database configuration dictionary. By doing this, we can dynamically load configuration values from environment variables.
The .env
file should be specific to the environment and not checked into version control. It is best practice documenting the .env
file with an example. For example, you can also add .env.sample
or sample.env
(however you feel comfortable naming it) with a template of your variables to the project repo. This file should describe the mandatory variables for the Django application, and it can be committed to version control. This provides a useful reference and speeds up the on-boarding process for new team members, since the time to dig through the codebase to find out what has to be set up is reduced.
A good sample.env
could look like this:
# SECURITY WARNING: don't run with the debug turned on in production!
DEBUG=True
# Should robots.txt allow everything to be crawled?
ALLOW_ROBOTS=False
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY=secret
# A list of all the people who get code error notifications.
ADMINS="John Doe <john@example.com>, Mary <mary@example.com>"
# A list of all the people who should get broken link notifications.
MANAGERS="Charles <charles@someorg.org>, Judy Smith <judy@someorg.org>"
# By default, Django will send system email from root@localhost.
# However, some mail providers reject all email from this address.
SERVER_EMAIL=webmaster@example.com
# Database
DATABASE_URL=postgres://username:password@localhost/db_name
django-environ provides a convenient way to customize configuration for different environments. You can create separate .env
files for each environment (e.g., .env.dev
, .env.prod
, etc.) and load them based on the current environment.
In your settings file, you can modify the loading of environment variables as follows:
# Determine the environment (defaulting to 'dev' if not set) ENVIRONMENT = env.str("ENVIRONMENT", default="dev") # Load environment-specific variables env_file = f".env.{ENVIRONMENT}" env.read_env(env_file) # Continue loading other configuration variables ...
In the code above, we introduce an ENVIRONMENT
variable to specify the current environment. We then dynamically construct the filename of the environment-specific .env
file and load it using env.read_env()
. This allows you to easily switch configurations based on the environment you're working in.
By using django-environ, you can align your Django project with the 12-factor principles and simplify configuration management. Storing configuration in environment variables allows for greater flexibility and portability across different environments. Additionally, separating configuration from code improves security and makes your project more scalable and maintainable.
Take advantage of django-environ in your Django projects and enjoy a streamlined and more robust configuration management experience.