Settings
Setting the timezone
You can set the timezone that will be used by Django in the settings.py
file. Examples:
TIME_ZONE = 'UTC' # use this, whenever possible
TIME_ZONE = 'Europe/Berlin'
TIME_ZONE = 'Etc/GMT+1'
Here is the list of valid timezones
When running in a Windows environment this must be set to the same as your system time zone.
If you do not want Django to use timezone-aware datetimes:
USE_TZ = False
Django best practices call for using UTC
for storing information in the database:
Even if your website is available in only one time zone, it’s still good practice to store data in UTC in your database. The main reason is Daylight Saving Time (DST). Many countries have a system of DST, where clocks are moved forward in spring and backward in autumn. If you’re working in local time, you’re likely to encounter errors twice a year, when the transitions happen.
https://docs.djangoproject.com/en/stable/topics/i18n/timezones/
Accessing settings
Once you’ve got all your settings, you’ll want to use them in your code. To do so, add the following import to your file:
from django.conf import settings
You may then access your settings as attributes of the settings
module, for example:
if not settings.DEBUG:
email_user(user, message)
Using BASE_DIR to ensure app portability
It’s a bad idea to hard code paths in your application. One should always use relative urls so that your code can work seamlessly across different machines. The best way to set this up is to define a variable like this
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
Then use this BASE_DIR
variable to define all your other settings.
TEMPLATE_PATH = os.path.join(BASE_DIR, "templates")
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
And so on. This ensures that you can port your code across different machines without any worries.
However, os.path
is a bit verbose. For instance if your settings module is project.settings.dev
, you will have to write:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
An alternative is to use the unipath
module (which you can install with pip install unipath
).
from unipath import Path
BASE_DIR = Path(__file__).ancestor(2) # or ancestor(3) if using a submodule
TEMPLATE_PATH = BASE_DIR.child('templates')
STATICFILES_DIRS = [
BASE_DIR.child('static'),
]
Using Environment variables to manage Settings across servers
Using environment variables is a widely used way to setting an app’s config depending on it environment, as stated in The Twelve-Factor App.
As configurations are likely to change between deployment environments, this is a very interesting way to modify the configuration without having to dig in the app’s source code, as well as keeping secrets outside the application files and source code repository.
In Django, the main settings are located as settings.py
in your project’s folder. As it is a simple Python file, you can use Python’s os
module from the standard library to access the environment (and even have appropriate defaults).
settings.py
import os
SECRET_KEY = os.environ.get('APP_SECRET_KEY', 'unsafe-secret-key')
DEBUG = bool(os.environ.get('DJANGO_DEBUG', True) == 'False')
ALLOWED_HOSTS = os.environ.get('DJANGO_ALLOWED_HOSTS', '').split()
DATABASES = {
'default': {
'ENGINE': os.environ.get('APP_DB_ENGINE', 'django.db.backends.sqlite3'),
'NAME': os.environ.get('DB_NAME', 'db.sqlite'),
'USER': os.environ.get('DB_USER', ''),
'PASSWORD': os.environ.get('DB_PASSWORD', ''),
'HOST': os.environ.get('DB_HOST', None),
'PORT': os.environ.get('DB_PORT', None),
'CONN_MAX_AGE': 600,
}
}
With Django you can change your database technology, so that you can use sqlite3 on your development machine (and that should be a sane default for committing to a source control system). Although this is possible it is not advisable:
Backing services, such as the app’s database, queueing system, or cache, is one area where dev/prod parity is important. (The Twelve-Factor App - Dev/prod parity)
For using a DATABASE_URL parameter for database connection, please take a look at the related example.
Using multiple settings
Django default project layout creates a single settings.py
. This is often useful to split it like this:
myprojectroot/
myproject/
__init__.py
settings/
__init__.py
base.py
dev.py
prod.py
tests.py
This enables you to work with different settings according to whether you are in development, production, tests or whatever.
When moving from the default layout to this layout, the original settings.py
becomes settings/base.py
. When every other submodule will “subclass” settings/base.py
by starting with from .base import *
. For instance, here is what settings/dev.py
may look like:
# -*- coding: utf-8 -*-
from .base import * # noqa
DEBUG = True
INSTALLED_APPS.extend([
'debug_toolbar',
])
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
INTERNAL_IPS = ['192.168.0.51', '192.168.0.69']
Alternative #1
For django-admin
commands to work properly, you will have to set DJANGO_SETTINGS_MODULE
environment variable (which defaults to myproject.settings
). In development, you will set it to myproject.settings.dev
. In production, you will set it to myproject.settings.prod
. If you use a virtualenv, best is to set it in your postactivate
script:
#!/bin/sh
export PYTHONPATH="/home/me/django_projects/myproject:$VIRTUAL_ENV/lib/python3.4"
export DJANGO_SETTINGS_MODULE="myproject.settings.dev"
If you want to use a settings module that is not pointed by DJANGO_SETTINGS_MODULE
for one time, you can use the --settings
option of django-admin
:
django-admin test --settings=myproject.settings.tests
Alternative #2
If you want to leave DJANGO_SETTINGS_MODULE
at its default configuration (myproject.settings
), you can simply tell the settings
module which configuration to load by placing the import in your __init__.py
file.
In the above example, the same result could be achieved by having an __init__.py
set to:
from .dev import *
Using multiple requirements files
Each requirements files should match the name of a settings files. Read Using multiple settings for more information.
Structure
djangoproject
├── config
│ ├── __init__.py
│ ├── requirements
│ │ ├── base.txt
│ │ ├── dev.txt
│ │ ├── test.txt
│ │ └── prod.txt
│ └── settings
└── manage.py
In base.txt
file, place dependencies used in all environments.
# base.txt
Django==1.8.0
psycopg2==2.6.1
jinja2==2.8
And in all other files, include base dependencies with -r base.txt
, and add specific dependencies needed for the current environment.
# dev.txt
-r base.txt # includes all dependencies in `base.txt`
# specific dependencies only used in dev env
django-queryinspect==0.1.0
# test.txt
-r base.txt # includes all dependencies in `base.txt`
# specific dependencies only used in test env
nose==1.3.7
django-nose==1.4
# prod.txt
-r base.txt # includes all dependencies in `base.txt`
# specific dependencies only used in production env
django-queryinspect==0.1.0
gunicorn==19.3.0
django-storages-redux==1.3
boto==2.38.0
Finally, to install dependencies. Example, on dev env : pip install -r config/requirements/dev.txt
Hiding secret data using a JSON file
When using a VCS such as Git or SVN, there are some secret data that must never be versioned (whether the repository is public or private).
Among those data, you find the SECRET_KEY
setting and the database password.
A common practice to hide these settings from version control is to create a file secrets.json
at the root of your project (thanks ”Two Scoops of Django” for the idea):
{
"SECRET_KEY": "N4HE:AMk:.Ader5354DR453TH8SHTQr",
"DB_PASSWORD": "v3ry53cr3t"
}
And add it to your ignore list (.gitignore
for git):
*.py[co]
*.sw[po]
*~
/secrets.json
Then add the following function to your settings
module:
import json
import os
from django.core.exceptions import ImproperlyConfigured
with open(os.path.join(BASE_DIR, 'secrets.json')) as secrets_file:
secrets = json.load(secrets_file)
def get_secret(setting, secrets=secrets):
"""Get secret setting or fail with ImproperlyConfigured"""
try:
return secrets[setting]
except KeyError:
raise ImproperlyConfigured("Set the {} setting".format(setting))
Then fill the settings this way:
SECRET_KEY = get_secret('SECRET_KEY')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgres',
'NAME': 'db_name',
'USER': 'username',
'PASSWORD': get_secret('DB_PASSWORD'),
},
}
Using a DATABASE_URL from the environment
In PaaS sites such as Heroku, it is usual to receive the database information as a single URL environment variable, instead of several parameters (host, port, user, password…).
There is a module, dj_database_url
which automatically extracts the DATABASE_URL environment variable to a Python dictionary appropriate for injecting the database settings in Django.
Usage:
import dj_database_url
if os.environ.get('DATABASE_URL'):
DATABASES['default'] =
dj_database_url.config(default=os.environ['DATABASE_URL'])