Splitting Django Settings for Local and Production Development

April 4, 2019 • Python Azure Django • 3 minutes to read • Edit

Splitting Django Settings for Local and Production Development

I have been working on a few projects that use Django 2+ and deploying them to Azure Docker container. I do have one problem though; my development settings are very much different from my production. So, I have divided settings.py into a package with - base.py, local.py and production.py:

<project name>
├── <project name>
│   ├── __init__.py
│   └── settings
│       ├── __init__.py
│       ├── base.py
│       ├── local.py
│       └── production.py
└── .dockerignore

.dockerignore file

While building a container, the CLI looks for .dockerignore file, which contains the file or file pattern to be ignored before sending the information to the Docker daemon. We can compare this to .gitignore, that ignores any file mentioned in this before committing into the version control.

This file ignores local.py.

You can learn more about it at their website -> https://docs.docker.com/engine/reference/builder/#dockerignore-file.

__init__.py file

Here, we check for local.py, if it exists this is imported and if not, production.py is used.

from .base import *

try:
    from .local import *

    live = False
except ImportError:
    live = True

if live:
    from .production import *

base.py file

This is the base for both local and production, the common code. Now it depends if you want to use the same SECRET_KEY on both production and local, I use it in both ways. If it’s a commercial project, I separate it and if not I put it in the base.py. Also, if you are using external logging providers such as Logstash or Sentry, you might want to separate LOGGING too.

The common code I use is:

BASE_DIR = ******
# This depends on you.
SECRET_KEY = ******

INSTALLED_APPS = [
    ...
]
MIDDLEWARE = [
    ...
]
ROOT_URLCONF = '******'
TEMPLATES = [
    ...
]
WSGI_APPLICATION = '******'
AUTH_PASSWORD_VALIDATORS = [
    ...
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
LOGGING = {
    ...
}
EMAIL_HOST = ******
EMAIL_PORT = ******
EMAIL_USE_SSL = ******
EMAIL_HOST_USER = ******
EMAIL_HOST_PASSWORD = ******

local.py file

I always separate my database, and I also make sure that they are the same type of database, in my case it’s PostgreSQL. Also, it contains the location for the static file and where the user can upload their files.

Code:

DEBUG = True

ALLOWED_HOSTS = ['*']

DATABASES = {
    ...
}

SHARE_URL = "http://127.0.0.1:8000"

# Static assets
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static', 'static_root')
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static', 'static_dirs'),
)

# User uploads
MEDIA_ROOT = os.path.join(BASE_DIR, 'static', 'media')

production.py file

This is where I use external services such as Azure, AWS or Heroku to add their production settings. I also restrict the access to one domain, disable DEBUG, use whitenoise for static assets and use dj_database_url for parsing PostgreSQL URL.

The code:

DEBUG = False

ALLOWED_HOSTS = ['******']

# Database connection to Azure URL
DATABASES = settings.DATABASES
DATABASES['default'] = dj_database_url.parse('******', conn_max_age=500,
                                             ssl_require=True)
# For django storages
AZURE_ACCOUNT_NAME = ******
AZURE_ACCOUNT_KEY = ******
AZURE_CONTAINER = ******

SHARE_URL = "******"

AZURE_BLOB_CUSTOM_DOMAIN = '******'

STATICFILES_LOCATION = 'static'
STATICFILES_STORAGE = '******'
STATIC_URL = "https://%s/%s/" % (AZURE_BLOB_CUSTOM_DOMAIN, STATICFILES_LOCATION)

MEDIAFILES_LOCATION = 'media'
DEFAULT_FILE_STORAGE = '******'
MEDIA_URL = "https://%s/%s/" % (AZURE_BLOB_CUSTOM_DOMAIN, MEDIAFILES_LOCATION)

Finally

One last tip is that I use my secrets as an environment variable or use the Azure Key Vault; this gives extra security and flexibility.

Happy coding.




comments powered by Disqus