Проект Django на Cloud Foundry не загружает CSS интерфейса администратора

У меня есть простой проект Django, развернутый с помощью Cloud Foundry. Интерфейс администратора (единственный интерфейс, используемый моим простым проектом) выглядит нормально при локальном запуске. Однако при развертывании CSS заметно нарушается. URL-адреса для CSS и JS возвращают 404.

Другие ответы на похожие вопросы говорят, что решением является команда collectstatic. Чтобы запустить ее в контексте Cloud Foundry, я набираю следующее (очевидно, с реальным именем приложения и путем):

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

Вывод, зарегистрированный при этом, выглядит очень многообещающе:

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

Однако, несмотря на это очевидное обещание, CSS по-прежнему не работает.

FYI, мои настройки включают следующее:

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')

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

Это не будет работать так, как вы хотите. Когда вы cf run-task, система запускает новый контейнер и выполняет вашу команду в этом новом контейнере. Команда будет выполняться и делать то, что должна, но когда задача завершится, она все это отбросит.

Я не знаком с DJango, поэтому не знаю, что делает эта команда или какие-либо нюансы, но у вас, вероятно, есть два варианта в контексте CloudFoundry.

Во-первых, запустите python app-path/manage.py collectstatic локально, прежде чем cf push. Если это какой-то процесс сборки, этого может быть достаточно, чтобы поместить файлы туда, куда им нужно.

Второе, запустите команду до запуска приложения. Это можно сделать двумя способами:

  1. Запустите cf push -c 'python app-path/manage.py collectstatic && python <your-normal-start-script.py>, заменив <your-normal-start-script.py> тем, что вы обычно запускаете. При этом сначала будет выполнена команда collectstatic, и если она пройдет, то будет запущена ваша обычная команда запуска. В качестве альтернативы вы можете установить command: в вашем manifest.yml.

  2. Добавьте .profile скрипт в корень вашего приложения. Внутри скрипта добавьте команду python app-path/manage.py collectstatic. Этот скрипт будет выполняться платформой перед запуском вашего приложения.

Из этих вариантов я рекомендую вариант №2, сценарий .profile, потому что он будет работать с любой командой, генерируемой Python buildpack для запуска вашего приложения. Таким образом, это меньше работы для вас.

Отвечая на свой вопрос: то, что я пытался сделать, просто не работает. Команда Django runserver обслуживает статические файлы только если DEBUG = True находится в соответствующем файле настроек, или если используется переключатель --insecure. Оба этих варианта считаются неприемлемыми для использования в развертывании.

Хотя в документации staticfiles утверждается, что приложение организует статические файлы, "которые можно легко обслуживать в продакшене", на самом деле это не так просто. В идеале документация должна пояснять, что collectstatic делает файлы доступными, только если веб-сервер настроен на их ожидание и обслуживание из любого места, куда их поместит команда.

Вот мои потенциальные пути продвижения вперед, как я их понимаю:

  1. Используйте веб-сервер, например, nginx, Gunicorn или Apache
  2. .
  3. Получать статические активы от облачного провайдера, например AWS S3
  4. Просто запустить collectstatic --insecure, что, очевидно, считается небезопасным
Вернуться на верх