Pixies, part 10: Serving Static Files


collectstatic

Django serves the static files only during the development, i.e. when you run python manage.py runserver. In the production, it is assumed that the static files are being served by something more suitable, like a regular Web server. Having said that, in interest of keeping our backend self-contained, there is a way to serve them by using the package called whitenoise Either way, the first step is to collect the static files from all Django apps we have (remember, our application is made of several Django apps, namely photos, admin and rest_framework). manage.py provide the command to do that, but first we have to tweak the settings.py a little. Add the following at the end pf your settings.py

STATIC_URL = "/static/"
STATIC_ROOT = str(BASE_LOC.path("staticfiles"))

These two settings specify that all static files will be served with the URL prefix /static/ and the location of the collected static files should be the subdirectory staticfiles Let’s collect the static files:

python manage.py collectstatic

You will notice now that the staticfiles directory has collected the css, png, js etc files from the rest_framework and admin apps. We are not using anywhere the static files in the photos app, so it was not created. Now install the whitenoise package which will allow to serve these without deferring to some web server:

pip install whitenoise

Now enable whitenoise middleware in the aettings.py. Because of the whitenoise requirements, it must go after the SecurityMiddleware

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    # new
    'whitenoise.middleware.WhiteNoiseMiddleware',
    # ...

whitenoise is able to cash and compress the static files it serves. To enable that add the following line:

# ...
STATIC_ROOT = str(BASE_LOC.path("staticfiles"))
# new
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

As we are still going to run Django in the dev mode, it is a good practice to disable the magical static file serving by the Django in the dev mode and go through the whitenoise even in such scenarios. In the INSTALLED_APPS section, just before the `django.contrib.staticfiles add:

    INSTALLED_APPS = [
    # ...
    # new
    "whitenoise.runserver_nostatic",
    "django.contrib.staticfiles",
    # ...

Now, let’s try to run whitenoise-enabled backend in the dev mode:

python manage.py collectstatic --noinput
python manage.py runserver

--noinput flag disables annoying verification questions. Verify that http://localhost:8000/admin/ still works as expected

All right, let’s modify our Docker image to use whitenoise as well. First we need to re-capture our dependencies:

pip freeze > requirements.txt

And now, modify the boot.sh file so it collects static files:

#!/bin/sh
python manage.py collectstatic --no-input
exec gunicorn pixies.wsgi:application --bind 0.0.0.0:$PORT --log-file -

Rebuild and start the containers again:

docker-compose up -d --build

Try connecting to the backend in the container at http://localhost:9090/admin/ This time it should display as expected with all css loaded.

While we are at it, let’s add the frontend to the docker-compose.yml so we can start everything together with one command:

# ...
# new
  frontend:
    build:
      context: ../frontend
    ports:
      - 9091:80
    environment:
      - PORT=80
    depends_on:
      - backend
#...

Now you can start everything in the containers just by running

docker-compose up -d --build

Your frontend should be available on the port 9091: http://localhost:9091/ Of course, it still shows the pictures we have hardcoded while writing the frontend code. In the next module, we are going to start connecting the frontend to the backend, so the picture list is loaded from the backend.


See also