I've read a lot about how to separate Django settings and how we should structure them. There is no one best path to this but there's one convenient idea that the developer should separate settings of the development and production environment. That's because;
- The development environment and the production environment might call for different dependencies. To give some examples, Django Debug Toolbar and Django Extensions libraries are meant to be used in the development environment rather than the production.
The development environment and the production environment might require different settings. For instance, SQLite database is easier to set up (almost zero configuration in Linux), but you might need to do extra work for other databases such as PostgreSQL or MariaDB on the development environment. You probably would like to go for SQLite in the development environment and you would set your development settings for SQLite while you would set your production environment for any other database. (See note 1)
Note 1
Of course, there could be different requirements. For instance, you could like to use PostgreSQL specific field such as
JSONField
,HStoreField
,ArrayField
orRangeField
. In that case, you would probably like to set your development environment upon PostgreSQL.
From this point on, the reasons are sound, so let's see how we can actually do that.
Separating Environments
For you to follow better, you can create a Django project on a temporary directory as such:
django-admin startproject myproject
So your project structure would look as below:
myproject/
myproject/settings.py
myproject/...
manage.py
Note
When I write a directory structure, I just highlight the directories and files that are necessary. Naturally,
myproject
directory would contain other files such as__init__.py
orwsgi.py
but they are not necessary, so they are pointed as triple dots in...
, which means that the directory in question can contain more files and directories.
Now, I'm writing what we are going to do step by step below:
- Create
myproject/settings/
directory. - Create
myproject/settings/__init__.py
file. - Move
myproject/settings.py
tomyproject/settings/
directory. - Rename moved
myproject/settings/settings.py
asdefaults.py
. - Create
myproject/settings/base.py
file. - Import everything from
myproject.settings.default
tomyproject.settings.base
. - Create
myproject/settings/development.py
file. - Import everything from
myproject.settings.base
tomyproject.settings.development
. - Create
myproject/settings/production.py
file. - Import everything from
myproject.settings.base
tomyproject.settings.production
. - Change
DJANGO_SETTINGS_MODULE
insidemanage.py
to point it atmyproject.settings.development
.
Now everything in this step can be achieved in bash, but through a point, it is possible.
export PROJECT_DIR="myproject"
mkdir "$PROJECT_DIR/settings" # (1)
touch "$PROJECT_DIR/settings/__init__.py" # (2)
mv "$PROJECT_DIR/settings.py" "$PROJECT_DIR/settings/defaults.py" # (3) and (4)
echo "from .defaults import *" > "$PROJECT_DIR/settings/base.py" # (5) and (6)
echo "from .base import *" > "$PROJECT_DIR/settings/development.py" # (7) and (8)
echo "from .base import *" > "$PROJECT_DIR/settings/production.py" # (9) and (10)
Regarding to the (11), you better do it with a text editor or IDE. Open up your editor, find the line that sets environment variable, it is similar to below:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
Change it to:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings.development")
Now, let's done so far from the graphic below:
In our environment, we should either use development
or production
. These import base
directory and defaults
indirectly. defaults
has the default settings, as you might guess.
How do I choose my environment?
When you execute python3 manage.py runserver
, Django will load with development
. This is the hardcoded default behavior since we've written it into manage.py
. You can switch this on production environment with setting DJANGO_SETTINGS_MODULE
value to myproject.settings.production
.
What is the reason for base
?
development
and production
, hence the names, contain settings related to development or production environment. So, a natural question that is hopefully on the reader's mind is probably "Why is there base.py
?".
If you overview the architecture above, then you can get a grasp of it. base
will contain the settings which is in both development and production environments. So, we will not duplicate the settings among development
and production
.
A Convention for Writing Settings
Now that we have created a skeleton to write our settings, it is time to write them.
In Django, you can think every aspect as a pluggable piece to your environment. Each aspect and third party library are actually a Django app, literally. You know you can create an app with python3 manage.py startapp
right? That's how Django dependencies are developed if you do not already know it.
So, let's assume that I am going to add Django Rest Framework to my project. Since I would want it in both development and production environment, I will put it inside base
as below:
# Rest Framework #
INSTALLED_APPS.append("rest_framework")
As you know, we created base
in a way that it imports defaults
and inside defaults
, we have a list
of app
s. We can actually append to that list instead of manually editing INSTALLED_APPS
in defaults
each time.
Also, Django Rest Framework has various settings that you can define in your settings, which is, in our case, base
. For instance, if you ever want to add settings, you can add it to the # Rest Framework #
section of your base
. The final result is:
# Rest Framework #
INSTALLED_APPS.append("rest_framework")
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
)
}
You probably also should add your internal apps to base
as well because they are usually needed both in development and production environments. In that case, assuming new app's name is foo
:
# foo #
INSTALLED_APPS.append("foo")
And if your app needs special settings, you can add it under this section.
As you might know, some apps should be visible to development environment. For instance, Django Debug Toolbar settings should be added with a section block into your development
instead of base
as below:
# Debug Toolbar #
INSTALLED_APPS.append("debug_toolbar")
Also, remember to set DEBUG
to False
in production
settings:
# Django Settings #
DEBUG = False
Final Words
This is what I came up with and apply in Django applications. As I have told, there are many more alternatives with different approaches and their own rationales. The reader might implement this approach as is or have the utter right to adapt it into their own preferences and environment.
Self Promotion
Have you wanted to receive the live log records with Telegram? I have a project called tglogger which contains a handler to send logs to Telegram chats. It has the features below as of the date I'm writing this:
- A custom formatter to send message with Telegram markdown
- This format also includes hashtags so that you can easily search log records in a chat
- System's date and time stamp
- Integration with Django
- Info about thread, process, file, method and function
The project status is still in beta, so any issue tickets and pull requests are welcome and also a star is appreciated.