7. Apply some refinements#
We now have a working application, that functions in multiple environments and takes its configuration for the services it requires from environment-provided variables.
However, there are a number of refinements we can make.
DEBUG using an environment variable#
DEBUG is hard-coded into the application code. This is not a good idea. We want to be sure that we don’t
inadvertently go into production with
DEBUG = True. So, let’s make it default to
False, and overwrite it only
where we need it to be True. First, in
and change the risky
DEBUG = True in
DEBUG = os.environ.get('DJANGO_DEBUG') == "True"
Your code can now be deployed with more confidence; only if the environment explicitly declares that Django can run in
debug mode will it do that (any other value for the environment variables than
True will evaluate to
Test locally; commit your changes once again, and redeploy and test on the cloud.
Earlier, we set
allows any host to serve the application, for convenience. This isn’t ideal -
ALLOWED_HOSTS exists to
mitigate fake Host header attacks, and even if this risk doesn’t apply in
Divio’s cloud hosting environments, it’s a bad idea to bake in configuration to your code that could be unsafe in
others. It’s better if we can restrict
ALLOWED_HOSTS to the right domains.
Each Divio cloud environment is provided with a DOMAIN environment variable, and (if the
application uses multiple domains) a DOMAIN_ALIASES environment variable. These can be used to configure
ALLOWED_HOSTS. You can see what environment variables have been set by using:
divio app env-vars --all
-s live option to see the variables for the Live environment.)
We can use these environment variables to populate
ALLOWED_HOSTS. Edit the settings file again:
DIVIO_DOMAIN = os.environ.get('DOMAIN', '') DIVIO_DOMAIN_ALIASES = [ d.strip() for d in os.environ.get('DOMAIN_ALIASES', '').split(',') if d.strip() ] DIVIO_DOMAIN_REDIRECTS = [ d.strip() for d in os.environ.get('DOMAIN_REDIRECTS', '').split(',') if d.strip() ] ALLOWED_HOSTS = [DIVIO_DOMAIN] + DIVIO_DOMAIN_ALIASES + DIVIO_DOMAIN_REDIRECTS
ALLOWED_HOSTS will always contain only the domains specified by the environment variables. On the cloud, these
are provided automatically; for the local development environment, we need to add the right ones to
Django’s secret key is hard-coded in our settings and committed to the repository. This is all right locally, but not
in production. However, since each cloud environment is provided with its own randomised SECRET_KEY variable, we can use that by changing
settings.py to use that (also providing a fall-back):
SECRET_KEY = os.environ.get('SECRET_KEY', '<a string of random characters>')
7.4. Configure SSL redirects#
In production, it’s almost always better to redirect to HTTPS, when the server supports it. However, we don’t want it when running locally. So, in settings, we will default to redirecting:
# Redirect to HTTPS by default, unless explicitly disabled SECURE_SSL_REDIRECT = os.environ.get('SECURE_SSL_REDIRECT') != "False"
And in the
.env-local, to disable it:
collectstatic to the build#
At the moment, we need to run
python manage.py collectstatic manually (and then commit the static files to the
This is a well-defined, repeatable task that is not really worthy of a human’s attention. It would be much more elegant
to have it executed automatically. We can do this using the
RUN pip install -r requirements.txt RUN python manage.py collectstatic --noinput CMD uwsgi --module=myapp.wsgi --http=0.0.0.0:80
However, if you try to run
docker-compose build now, you’ll run into an error. During the build process, Docker has
no access to environment variables - including the ones it’s expecting to use to define settings such as
This is how it should be: building a Docker image should not be dependent upon any particular environment conditions.
The solution is to build a fallback into the Django settings by editing the line that determines the
DATABASE_URL = os.environ.get('DATABASE_URL', 'sqlite://:memory:')
Now when no
DATABASE_URL can be found in the environment, Django will happily use a dummy backend instead.
You can test it by building again.
/staticfiles directory no longer needs to be a part of the repository. Remove it:
git rm -r staticfiles
Commit and push the code changes, and run a deployment to check results. From now on, even if you add new applications with their own static files, or change the static files in existing applications, they will be collected automatically on deployment.
Serving static files, locally
When working locally, the static files collected by Docker inside the container will be overwritten, because of
volumes: - ".:/app:rw"
as soon as Docker Compose is invoked. This doesn’t matter when you’re using Django in debug mode, because it
will take care of static files for you, but if you’re trying to work with
DEBUG = False and need your
static files to be served, you’ll have to re-create them by running:
docker-compose run web python manage.py collectstatic
This completes the basic cycle of application creation, development and deployment, and how to integrate multiple cloud services into an application. You should now be familiar with the fundamental concepts and tools involved.
Other sections of the documentation expand upon these topics. The how-to guides in particular cover many common operations. And if there’s something you’re looking for but can’t find, please contact Divio support.