An Approach on Django Settings

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 or RangeField. 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 or wsgi.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:

  1. Create myproject/settings/ directory.
  2. Create myproject/settings/__init__.py file.
  3. Move myproject/settings.py to myproject/settings/ directory.
  4. Rename moved myproject/settings/settings.py as defaults.py.
  5. Create myproject/settings/base.py file.
  6. Import everything from myproject.settings.default to myproject.settings.base.
  7. Create myproject/settings/development.py file.
  8. Import everything from myproject.settings.base to myproject.settings.development.
  9. Create myproject/settings/production.py file.
  10. Import everything from myproject.settings.base to myproject.settings.production.
  11. Change DJANGO_SETTINGS_MODULE inside manage.py to point it at myproject.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:

Django Settings Architecture

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 apps. 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.