我已经构建了一个仅提供 JSON 响应的 Django REST API。我的问题是,在生产中(使用 debug=False 部署在 Heroku 上),该应用程序似乎没有提供适当的管理界面样式所需的相关静态文件(仅用于静态文件的用例)。请注意,在开发中(带有 debug=True 的 localhost),管理界面的样式正确。
转到已部署(Heroku)地址的管理路由,内容已交付,但没有任何样式。浏览器开发人员工具指示由于 500 错误代码而无法加载样式表。Django 日志输出显示以下详细信息。
django.request ERROR Internal Server Error: /static/admin/css/base.1f418065fc2c.css
Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/app/secure_my_spot/custom_middleware/request_logging.py", line 30, in __call__
print(f"Content: {response.content}")
File "/usr/local/lib/python3.8/site-packages/django/http/response.py", line 407, in content
raise AttributeError(
AttributeError: This WhiteNoiseFileResponse instance has no `content` attribute. Use `streaming_content` instead.
我已经进入 Heroku dyno 并验证了导致 500 错误的静态文件实际上根据 Django 的 settings.py 位于 static_root 中。我一直在花费大量时间在互联网上搜索可能导致文件无法在生产中提供的线索,但无论我尝试过什么,它都不起作用。
以下是相关文件和设置的精选摘要。
Dockerfile
FROM python:3.8-alpine
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN apk update && apk add \
gcc \
libc-dev \
python3-dev \
musl-dev \
postgresql-dev \
vim \
bash --no-cache --upgrade
RUN pip install \
pipenv \
psycopg2
COPY Pipfile Pipfile.lock ./
RUN pipenv install --system --deploy --pre
COPY . .
RUN python manage.py collectstatic --noinput
CMD gunicorn secure_my_spot.wsgi:application --bind 0.0.0.0:$PORT
heroku.yml
build:
docker:
web: Dockerfile
release:
image: web
command:
- chmod u+x heroku_entrypoint.sh
run:
web: ./heroku_entrypoint.sh
heroku_entrypoint.sh
#!/bin/bash
python manage.py collectstatic --noinput
python manage.py migrate --noinput
gunicorn secure_my_spot.wsgi:application --bind 0.0.0.0:$PORT
设置.py
BASE_DIR = Path(__file__).resolve().parent.parent
INSTALLED_APPS = [
"django_extensions",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework",
"rest_framework.authtoken",
"app",
"corsheaders",
]
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
"secure_my_spot.custom_middleware.request_logging.RequestLogging",
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles/")
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
wsgi.py
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "secure_my_spot.settings")
if settings.DEBUG:
application = StaticFilesHandler(get_wsgi_application())
else:
application = get_wsgi_application()