Django project on Cloud Foundry not loading admin interface CSS

217 Views Asked by At

I have a simple Django project deployed with Cloud Foundry. The admin interface (the only interface used by my simple project) looks fine when running locally. When deployed, however, the CSS is visibly broken. The URLs for the CSS and JS assets return 404.

Other answers to similar questions say that the solution is the collectstatic command. To run this in the context of Cloud Foundry, I type the following (obviously with the real app name and path):

cf run-task app-name -c "python app-path/manage.py collectstatic"

The output logged from this looks very promising:

128 static files copied to '/home/...path.../settings/static'.

Yet, in spite of this apparent promise, the CSS is still broken.

FYI, my settings include this:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

STATIC_URL = '/static/'

PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
STATIC_ROOT = os.path.join(PROJECT_DIR, 'static')
2

There are 2 best solutions below

1
On BEST ANSWER

Answering my own question: what I was trying to do simply does not work. Django's runserver command only serves static files if DEBUG = True is in the appropriate settings file, or if the --insecure switch is used. Both of these are considered inappropriate for deployment use.

Although the staticfiles documentation claims the app organizes static files "that can easily be served in production" it's not really so easy. Ideally, the documentation should clarify that collectstatic only makes files available if the web server is configured to expect them and serve them from wherever the command puts them.

Here are my potential paths forward as I understand them:

  1. Use a web server like nginx, Gunicorn, or Apache
  2. Serve static assets from a cloud provider like AWS S3
  3. Simply run collectstatic --insecure which, obviously, is considered insecure
1
On

cf run-task app-name -c "python app-path/manage.py collectstatic"

This isn't going to work the way you want. When you cf run-task the system is starting a new container and running your command in that new container. The command is going to run and do what it's supposed to but when the task ends, it'll throw that all away.

I'm not familiar with DJango, so I don't know what that command is doing or any nuance about it, but you probably have two options here in the context of CloudFoundry.

The first, run python app-path/manage.py collectstatic locally before you cf push. If it's some sort of build process, that might be sufficient to put files where they need to go.

The second, Run the command before your application starts. There are two ways you can do this:

  1. Run cf push -c 'python app-path/manage.py collectstatic && python <your-normal-start-script.py>, replacing <your-normal-start-script.py> with what you normally run. This will execute the collectstatic command first and if it passes then run your usual start command. You could alternatively set command: in your manifest.yml.

  2. Add a .profile script to the root of your application. Inside the script, add the command python app-path/manage.py collectstatic. This script will be executed by the platform before your application starts up.

Of these options, I recommend option #2, the .profile script, because it'll work with whatever command is being generated by the Python buildpack to run your application. Thus it's less work for you.