42
loading...
This website collects cookies to deliver better user experience
from django.conf import settings
settings
import is a module level object created in django/conf/__init__.py
. The settings
object has attributes added to it from two primary sources.django/conf/global_settings.py
and provide a set of initial values for configuration that Django needs to operate.settings
object. To find the user module, Django searches for a DJANGO_SETTINGS_MODULE
environment variable.env
command on macOS or Linux, or the set
command on Windows.export
command on macOS or Linux, or the set
command on Windows. Environment variables are typically named in all capital letters.$ export HELLO=world
DJANGO_SETTINGS_MODULE
variable. The variable's value should be the location of a Python module containing any settings that a developer wants to change from Django's default values.startproject
and use project
as the name, then you will find a generated file called project/settings.py
in the output. When Django runs, you could explicitly instruct Django with:$ export DJANGO_SETTINGS_MODULE=project.settings
DJANGO_SETTINGS_MODULE
should be in a Python module dotted notation.DJANGO_SETTINGS_MODULE
explicitly. If you stick with the same settings file that is created by startproject
, you can find a line in wsgi.py
that looks like:os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
project.settings
(or whatever you named your project) without the need to explicitly set DJANGO_SETTINGS_MODULE
.settings
object via attribute access. This convention of keeping all configuration in the settings
object is a convenient pattern that the framework, third party library ecosystem, and you can depend on.$ ./manage.py shell
>>> from django.conf import settings
>>> settings.SECRET_KEY
'a secret to everybody'
settings
object is a shared item so it is generally thought to be a Really Bad Idea™ to edit and assign to the object directly. Keep your settings in your settings module!DEBUG = True
is a terrible idea for a live Django site, so how can we get the benefits of the debug mode without having DEBUG
set to True
in our module?DJANGO_SETTINGS_MODULE
value to pick a different environment. You might have modules like:project.settings.dev
project.settings.stage
project.settings.production
*
import. I can probably count on one hand the number of places where I'm ok with a *
import, and this is one of them. In most cases the Python community prefers explicit over implicit, and the idea extends to the treatment of imports. Explicit imports make it clear what a module is actually using. The *
import is very implicit, and it makes it unclear what a module uses. For the case of a common settings module, a *
import is actually positive because we want to use everything in the common module.project.settings.base
module. This module would hold your common settings for your app. I'd recommend that you try to make your settings safe and secure by default. For instance, use DEBUG = False
in the base settings and force other settings modules to opt-in to the more unsafe behavior.project.settings.dev
. This settings module would look like:# project/settings/dev.py
from project.settings.base import *
DEBUG = True
# Define any other settings that you want to override.
...
*
import in the dev.py
file, all the settings from base.py
are pulled into the module level scope. Where you want a setting to be different, you set the value in dev.py
. When Django starts using DJANGO_SETTINGS_MODULE
of project.settings.dev
, all the values from base.py
will be used via dev.py
.os
module. The module contains the environ
attribute, which functions like a dictionary.# project/settings.py
import os
SECRET_KEY = os.environ['SECRET_KEY']
...
startproject
output that reads:# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY
, we won't be committing the value to source control for some nefarious actor to discover.EMAIL_BACKEND
setting. This let's me switch between the Anymail backend and Django's built-in django.core.mail.backends.console.EmailBackend
that prints emails to the terminal instead.os.environ
, it would look like:# project/settings.py
import os
EMAIL_BACKEND = os.environ.get(
'EMAIL_BACKEND', "anymail.backends.sendgrid.EmailBackend")
...
DEBUG = False
), but it also means that my live site has less to configure. That's good because there are fewer chances to make configuration mistakes on the site that matters most: the one where my customers are.str
type. This is something to be aware because there will be times when you want a boolean settings value or some other type of data. In a situation where you need a different type, you have to coerce a str
into the type you need. In other words, don't forget that every string except the empty string is truthy in Python:>>> not_false = "False"
>>> bool(not_false)
True
diffsettings
command. This tool makes it easy to see the computed settings of your module. Since settings can come from multiple files (including Django's global_settings.py
) or environment variables, inspecting the settings output of diffsettings
is more convenient than thinking through how a setting is set.diffsettings
will show a comparison of the settings module to the default Django settings. Settings that aren't in the defaults are marked with ###
after the value to indicate that they are different.diffsettings
to output in a "unified" format. This format looks a lot more like a code diff. In addition, Django will colorize that output so that it's easier to see. Here's an example of some of the security settings by running ./manage.py diffsettings --output unified
for one of my projects.- SECURE_HSTS_INCLUDE_SUBDOMAINS = False
+ SECURE_HSTS_INCLUDE_SUBDOMAINS = True
- SECURE_PROXY_SSL_HEADER = None
+ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
$ ./manage.py diffsettings \
--default project.settings.dev \
--settings project.settings.prod \
--output unified
--default
flag, we instruct Django that project.settings.dev
is the baseline for comparison. This version of the command will show where the two settings modules are different.django-environ
, you start with Env
object.# project/settings.py
import environ
env = environ.Env()
Env
describe the different environment variables that you expect the app to process. The key is the name of the environment variable. The value is a two element tuple. The first tuple element is the type you want, and the second element is a default value if the environment variable doesn't exist.DEBUG
from an environment variable, the settings would be:# project/settings.py
import environ
env = environ.Env(
DEBUG=(bool, False),
)
DEBUG = env("DEBUG")
DEBUG
set to False
, but you'll be able to override that via the environment. django-environ
works with a handful of strings that it will accept as True
such as "on", "yes", "true", and others (see the documentation for more details).export
for all your variables before running your app is a totally unsustainable way to run apps.Env
class comes with a handy class method named read_env
. With this method, your app can read environment variables into os.environ
from a file. Conventionally, this file is named .env
, and the file contains a list of key/value pairs that you want as environment variables. Following our earlier example, here's how we could set our app to be in debug mode:# .env
DEBUG=on
read_env
:# project/settings.py
import environ
environ.Env.read_env()
env = environ.Env(
DEBUG=(bool, False),
)
DEBUG = env("DEBUG")
.env
file, you will occasionally find a need to put secrets into this file for testing. Since the file can be a source for secrets, you should add this to .gitignore
or ignore it in whatever version control system you use. As time goes on, the list of variables and settings will likely grow, so it's also a common pattern to create a .env.example
file that you can use as a template in case you ever need to start with a fresh clone of your repository.django-environ
in a single file is the best pattern in my experience.$ EMAIL_TESTING=on ./manage.py runserver
.env
to test real emails for the local environment, I don't want my tests to send out an emails accidentally.# project/testing_settings.py
from .settings import *
# Make sure that tests are never sending real emails.
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
Env
will look for an EMAIL_BACKEND
environment variable to configure that setting dynamically, the testing setting is hardcoded to make email sending accidents impossible.