Table of Content
- How to Configure Settings for Different File Environments
- How to Set Up Django Server to Use Different Settings in Different Environments
- Additional things to do
- Wrap Off
When you create a new project in Django, you are provided with a default settings file with some parameters and constants already set.
However, if you have any intention of taking your project to a production or staging environment, it is a good idea to break down your setting into multiple configuration files.
This is because as a Django project develops in apps and functionality, the settings.py
module can get fairly complex over time.
Although some developers tend to use conditional if
statements to separate configuration parameters and variables.
In those cases, you also want to avoid using if
statements like if not DEBUG: # do something...
.
For clarity, it is important to make a strict distinction between what is development configuration and what is production configuration.
This is done effectively by breaking down the settings.py
module into multiple files.
Let's take a brand new Django project structure, which looks like the tree structure below. Here's how you can split the settings file into multiple configuration files:
src/
|-- core/
| |-- __init__.py
| |-- asgi.py
| |-- settings.py
| |-- urls.py
| +-- wsgi.py
+-- manage.py
1. Create a directory named “settings” within the core directory
The first thing we want to do is to create a folder named settings
.
2. Add a __init__.py
file to the settings directory
The __init__.py
will make the settings directory a python module.
3. Rename the previous settings.py
file to base.py
and move it into the settings directory
The base.py
will be responsible for providing the settings configuration that is common among all environments such as development, production, staging, etc.
Here is how the current project structure should look after the three steps above.
src/
|-- core/
| |-- __init__.py
| |-- asgi.py
| |-- settings/ <--
| | |-- __init__.py <--
| | +-- base.py <--
| |-- urls.py
| +-- wsgi.py
+-- manage.py
4. Create a settings.py
module for each environment.
You should create settings.py
for all common environments using the name of the environment as the file name.
Some common use cases are:
- ci.py
This file will contain configurations of continuous integration (CI/CD) or tests. You can usetests.py
in place ofci.py
. - development.py
This file will contain configurations of the Development version alone. It is sometimes namedlocal.py
ordev.py
. - production.py
This file will contain configurations of the production environment. It is sometimes namedprod.py
. - staging.py
This file will contain configurations of the staging environment.
It is important to know that the file names are merely conventional and not enforced. You can call it whatever you like, as long it adheres to python's file naming rules.
Here's how the file structure would look like after creating all the files representing the various environments above:
src/
|-- core/
| |-- __init__.py
| |-- asgi.py
| |-- settings/
| | |-- __init__.py
| | |-- base.py
| | |-- ci.py
| | |-- development.py
| | |-- production.py
| | +-- staging.py
| |-- urls.py
| +-- wsgi.py
+-- manage.py
How to Configure Settings for Different File Environments
Here's how you would go about setting the configurations for the development, production, and base settings.
To do this, we will use the decouple package.
Decouple helps you to organize your settings so that you can change parameters without having to redeploy your app.
Decouple adheres to the following principles, which we will follow:
- Store parameters in ini or .env files.
- Define comprehensive default values.
- Convert values to the correct data type.
- Have only one configuration module to rule all your instances.
Install the decouple package using the code below
First, let's start with the base.py
module in settings/base.py.
We will only define a handful of settings, so the example does not get too big.
from decouple import config
SECRET_KEY = config('SECRET_KEY')
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'src.estore',
'src.blog',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'core.urls'
WSGI_APPLICATION = 'core.wsgi.application'
Next, we will create a development.py
configuration by first extending our base.py
settings module.
In settings/development.py,
from .base import *
DEBUG = True
INSTALLED_APPS += [
'debug_toolbar',
]
MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware', ]
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEBUG_TOOLBAR_CONFIG = {
'JQUERY_URL': '',
}
Next, we will define a production.py
configuration, which also extends the base.
In settings/production.py,
from .base import *
DEBUG = False
ALLOWED_HOSTS = ['ourgamechangerapplication.com', ]
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.mailgun.org'
EMAIL_PORT = 587
EMAIL_HOST_USER = config('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD')
EMAIL_USE_TLS = True
When working with Python, it is generally advised to avoid using star import, which indicates, importing everything.
This is because star imports may put lots of unnecessary stuff in the namespace, which in some cases can cause issues.
However, importing all from Django base settings is one of the few cases where it is accepted.
When importing everything from the base, it is important to know that
Also bear in mind that even though we are using different files for development and production, you still have to protect sensitive data.
Keep passwords and secret keys in environment variables or use a library like Python-Decouple which is used in this example.
How to Set Up Django Server to Use Different Settings in Different Environments
Since we no longer have a single settings.py
file in the project root, running the command: python manage.py runserver
will no longer work unless we make some changes.
There are three ways you can do this:
Method One: Run the server with the settings you want to use
Here, you have to pass the settings.py
module you want to use in the command line:
python manage.py runserver --settings=mysite.settings.development
Or
python manage.py migrate --settings=mysite.settings.production
Method Two: Configure it in manage.py
You can edit the manage.py
to set the default settings module to your development.py
module.
To do that, simply edit the manage.py
file, like this:
In manage.py,
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings.development")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()
So, basically, we changed the line from:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")
To:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings.development")
Now you can run the manage.py
commands again without using the --settings
argument.
Remember, with this approach, you have to refer to the correct settings module in production!
Method Three: Configure it in settings\__init__.py
This solution works with the idea that the development file will not be in the production environment.
Therefore, you should remember to add settings\development.py
to .gitignore
, when deploying using a version control system.
Additional things to do
Since we have different settings modules, you can move the AUTH_PASSWORD_VALIDATORS
from the settings/base.py
and only add it to the settings/production.py
module.
This will allow you to use simple passwords like “123” during development, but in the production environment, it will be protected by the validators.
Also, in your settings/tests.py
or settings/ci.py
, you can override the following configuration, so your tests run faster:
DATABASES['default'] = {
'ENGINE': 'django.db.backends.sqlite3'
}
PASSWORD_HASHERS = (
'django.contrib.auth.hashers.MD5PasswordHasher',
)
Wrap Off
I hope you found this post useful somehow!
If you have any questions or need clarification, feel free to reach out to me.
If you learned from this tutorial, or it helped you in any way, please consider sharing and subscribing to our newsletter.