How to Have Different Django Settings for Development and Production, and environment isolation
Having a different configuration for development, staging and production servers
Overview
Django projects should have several environments each with its own peculiarities. Each environment is optimized for a specific task, e.g.: develop your app, having packages that helps you test it, another with all the security measures for production, and so on.
In this guide we will split the standard settings file into specific settings optimized for each environment.
A common deployment architecture consists of a 4-tier architecture, having different settings for one of these tiers:
- development (DEV)
- testing (TEST)
- staging (STAGE)
- production (PROD)
A default Django app just starts with a single configuration file
located in DJANGO_PROJECT/settings.py
. This approach is fine for
small projects but to fit in the above 4-tier architecture, the
project needs to be changed to address two main problems:
- each environment should have a specific settings file
- each environment should have its own packages
However there are special config keys that should be left out of versioning, like the SECRET_KEY setting, used for cryptographic signing functionalities.
Process
Settings
There are two main approaches to work with settings in different environments:
- Use environment variables
- split
settings.py
file into multiple files and use each one in each different environment
It is better to have sensitive keys as environment variables whenever possible so it leaves secret keys outside config files versioning using environment variables as recommended by http://12factor.net/config.
Splitting settings description
The default Config file that comes shipped with Django should be pulled apart into several settings for each environment: local, staging, test, production.
This can be done easily inheriting from a base config file, changing what the specific environment needs
Using different settings
To set SECRET_KEY
and DJANGO_DEBUG
variables in each environment
add this to settings.py
.
# Read SECRET_KEY from an environment variable
import os
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'p@1a;sljfk=$adsk;lhPUO*R*P(@HBBGG)g')
DEBUG = bool( os.environ.get('DJANGO_DEBUG', True) )
This will set a default value for the SECRET_KEY variable at low
security environments like development and testing, then you will have
to set DJANGO_SECRET_KEY
and DJANGO_DEBUG
variables in production.
Variables can be set with environment variables e.g.: export
A_SECRET_KEY=foobar1234
$ export A_SECRET_KEY=shhh1234
, and placed in:
- .bashrc or .profile
- virtualenvwrapper’s bin/activate hook file
echo "export
DJANGO_SETTINGS_MODULE=settings.local" >>
$VIRTUAL_ENV/bin/postactivate
When using manage.py many commands accepts the parameter to specify
a specific settings file: python manage.py runserver --settings=myproject.settings.local
$ manage.py
diffsettings
so you can see what you have changed from the original
settings file so far.Checking settings environment variables
To check that the secret environment key is being loaded, it is possible to check it from a python shell:
$ ./manage.py shell Python 3.6.4 (default, Feb 5 2018, 16:52:44) [GCC 7.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> import os >>> os.environ["A_SECRET_KEY"] "shhh1234"
Then to get the value for a specific environment, the production config
file in version control only needs to get this environment variable
value: A_SECRET_KEY = os.environ["A_SECRET_KEY"]
$ heroku config:set A_SECRET_KEY=shhh1234
Creating different settings files
When building a project, Django automatically creates a configuration
file in $PROJECT_NAME/settings.py
.
To split this file into multiple settings files (one for each
environment local, testing, staging and production), the best
way is to create a base.py
config with common settings across all of
them and create specific config files for each environment in specific
files having a structure like:
.
βββ REPO-ROOT (git repo)
βββ ...
βββ PROJECT-ROOT
βββ settings
| βββ __init__.py
| βββ base.py
| βββ local.py
βββ manage.py
βββ ...
To build this structure:
1. Create directory
Create the settings directory
$ mkdir $PROJECT_NAME/settings
2. Add init.py
Add __init__.py
file to make this directory a Python package.
$ touch $PROJECT_NAME/settings/__init__.py
3. Create base.py from settings.py
Move the default settings file into the new settings directory and change its name.
$ mv $PROJECT_NAME/settings.py $PROJECT_NAME/settings/base.py
4. Create setting files
Create all the configuration files (local.py
, testing.py
,
staging.py
, production.py
) and specify to inherit base.py
configurations adding from .base import *
to each file:
$ for name in local.py testing.py staging.py production.py; do echo "from .base import *" >> $PROJECT_NAME/settings/${name} ; done
So the directory tree structure is:
myapp/settings/
βββ base.py
βββ __init__.py
βββ local.py
βββ production.py
βββ staging.py
βββ testing.py
Also, in app/settings/base.py
you can have the following helpful variable
to work with your project main path:
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# or
BASE_DIR = Path(__file__).resolve(strict=True).parent.parent.parent
5. Use new settings file
Use the new settings file in one of these ways:
- call scripts specifying a settings file as mentioned earlier
- set an environment variable and call scripts normally
5.1 Set environment variables
Configure the current environment to use the appropriate settings file, using PYTHONPATH and DJANGO_SETTINGS_MODULE:
$ export DJANGO_SETTINGS_MODULE=mysite.settings.local $ export PYTHONPATH=~/path/to/my/project #django-admin will use the above settings by default $ django-admin runserver
Use the --settings
parameter with manage.py
or django-admin
:
$ django-admin runserver --settings=mysite.settings.local --pythonpath=/path/to/my/project
Performing system checks...
System check identified no issues (0 silenced).
July 23, 2016 - 22:43:48
Django version 1.9.6, using settings 'mysite.settings.local'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Differences between manage.py and django-admin
Django has two administrative scripts: django_admin.py
and manage.py
(in the root of each Django project).
manage.py
is a wrapper of
django-admin
, the only difference
between them is that manage.py
sets the DJANGO_SETTINGS_MODULE environment variable to settings.py
by default, if not changed.We use the new path in manage.py
, changing this
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings.local')
to os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings.local')
Generally, when working on a single Django project, itβs easier to use manage.py than django-admin. If you need to switch between multiple Django settings files, use django-admin with **DJANGO_SETTINGS_MODULE** or the **--settings** command line option.
Packages for each environment
Now a similar approach for handling package dependencies.
Each environment has to have a set of packages that fits its purpose and operating system requirements. We have to configure a python virtual environment so it is possible to install packages in development, that are not needed in production and viceversa, or that can be installed in different Operating Systems.
Each environment needs a specific file, having a base.txt requirement file with common packages across environments and then adding the needed packages for each environment.
.
βββ REPO-ROOT git repo
βββ .gitignore
βββ requirements
| βββ base.txt
| βββ local.txt
| βββ production.txt
| βββ test.txt
βββ ...
βββ PROJECT-ROOT
βββ manage.py
βββ ...
Using pip
it is possible to specify which file has the list of
packages you want:
pip install [options] -r <requirements file> [package-index-options] ...
-r, --requirement <file> Install from the given requirements file. This option can be used multiple times.
So to make it possible for each environment to inherit the packages from the
base.txt requirement file using pip
, each new file should begin with: -r base.txt
:
/requirements/base.txt
:
# list of packages present in all environments
/requirements/local.txt
:
-r base.txt
/requirements/test.txt
:
-r base.txt
/requirements/production.txt
:
-r base.txt
- To generate a requirements file: $ pip freeze
$ pip freeze --local > requirements/base.txt
- To install the requirements file
- in a local/development environment:
$ pip install -r requirements/local.txt
- in a testing environment:
$ pip install -r requirements/testing.txt
- in a local/development environment:
Conclusion
We started having a settings.py
single file and break it up into a new
directory with specific environment settings.
As with any major change to the default installation, after generating
these directories, it is a good practice to describe them in
/docs/architecture.rst
and what are the commands used to get them
running in /docs/installation.rst
for other developers or just for
oneself when reviewing the project in the future.
with custom settings for each environment"] split==>pack["Configure different Packages for each environment"]
Github
There is a Github with all of these steps applied to show how the project skeleton would look like at https://github.com/marcanuy/django-architecture-example
References
- Wikipedia 4-tier architecture
- https://docs.djangoproject.com/en/1.9/topics/settings/
- https://docs.djangoproject.com/en/1.9/ref/settings/
- https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Deployment
- August 1, 2023
- How to create a reusable Django app and distribute it with PIP or publish to pypi.orgJune 29, 2021
- How To Serve Multiple Django Applications with uWSGI and Nginx in Ubuntu 20.04October 26, 2020
- How to add favicon to Django in 4 stepsSeptember 3, 2020
- Categories in Django with BreadcrumbsAugust 30, 2020
- How To Migrate From SQLite To PostgreSQL In Django In 3 stepsAugust 28, 2020
- Practical guide to internationalize a Django app in 5 steps.August 24, 2020
- Disable new users singup when using Django's allauth packageSeptember 3, 2019
- How to add ads.txt to Django as requested by Google AdsenseAugust 30, 2019
- Have multiple submit buttons for the same Django formJuly 2, 2019
- Better Testing with Page Object Design in DjangoMay 1, 2019
- Generating slugs automatically in Django without packages - Two easy and solid approachesFebruary 14, 2019
- How to set up Django tests to use a free PostgreSQL database in HerokuFebruary 13, 2019
- Dynamically adding forms to a Django FormSet with an add button using jQueryFebruary 6, 2019
- Use of Django's static templatetag in css file to set a background imageFebruary 1, 2019
- Activate Django's manage.py commands completion in Bash in 2 stepsJanuary 29, 2019
- Sending Emails with Django using SendGrid in 3 easy stepsJanuary 9, 2019
- Adding Users to Your Django Project With A Custom User ModelSeptember 21, 2018
- Setting Up A Factory For One To Many Relationships In FactoryboyApril 17, 2018
- Generate UML class diagrams from django modelsMarch 24, 2018
- Set Up Ubuntu To Serve A Django Website Step By StepJuly 3, 2017
- Django Project Directory StructureJuly 16, 2016
- How to Have Different Django Settings for Development and Production, and environment isolation
- Django OverviewJune 2, 2016
Django Forms
- Adding a Cancel button in Django class-based views, editing views and formsJuly 15, 2019
- Using Django Model Primary Key in Custom Forms THE RIGHT WAYJuly 13, 2019
- Django formset handling with class based views, custom errors and validationJuly 4, 2019
- How To Use Bootstrap 4 In Django FormsMay 25, 2018
- Understanding Django FormsApril 30, 2018
- How To Create A Form In DjangoJuly 29, 2016
Articles
Subcategories
Except as otherwise noted, the content of this page is licensed under CC BY-NC-ND 4.0 . Terms and Policy.
Powered by SimpleIT Hugo Theme
·