在Heroku上使用whitenoise时,Django的静态文件未找到。

21

这个问题似乎已经被问过多次,但我无法解决它。

我在生产环境中部署了一个 Django 应用程序,DEBUG=False。我设置了我的 allowed_host。我使用 {% load static from staticfiles %} 加载静态文件。我完全按照 Heroku 文档建议的设置编写了代码:

BASE_DIR = os.path.dirname(os.path.dirname(__file__))
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))

STATIC_ROOT = os.path.join(PROJECT_ROOT, 'staticfiles')
STATIC_URL = '/static/'

STATICFILES_DIRS = (
    os.path.join(PROJECT_ROOT, 'static'),
)

STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage'

但是我遇到了500错误,邮件中附带了以下回溯信息:

...
`cache_name = self.clean_name(self.hashed_name(name))
 File "/app/.heroku/python/lib/python3.5/site-    packages/django/contrib/staticfiles/storage.py", line 94, in hashed_name (clean_name, self))
...
ValueError: The file ‘app/css/font.css’ could not be found with <whitenoise.django.GzipManifestStaticFilesStorage object at 0x7febf600a7f0>.`

当我运行 heroku run python manage.py collectstatic --noinput 时,一切都看起来正常:

276个静态文件被复制到'/app/annuaire/staticfiles',276个被处理。

有没有人能帮我想个主意,谢谢?

谢谢

编辑:

annuaire
|-- /annuaire
|-- -- /settings.py
|-- /app
|-- -- /static/...`

wsgi.py

from django.core.wsgi import get_wsgi_application
from whitenoise.django import DjangoWhiteNoise


application = get_wsgi_application()
application = DjangoWhiteNoise(application)

1
Django通常只在DEBUG=False时调用该特定函数,因此我很困惑您在DEBUG=True时会遇到该错误。我建议您在本地使用DEBUG=False测试应用程序,运行“collectstatic”,然后运行“runserver”以查看是否会出现该错误。这可能是因为您还没有提交该特定文件吗? - D. Evans
你是否打印了 STATIC_ROOT 来确保它与 '/app/annuaire/staticfiles' 相同? - agconti
它不起作用...现在当我在Heroku上运行collectstatic时,我得到了FileNotFoundError:[Errno 2] No such file or directory:'/app/annuaire/static' - vpoulain
我的 / 页面无法正常工作,而 /admin 页面可以正常渲染。此外,我已经检查过了,我的静态文件可以被 Web 服务器返回:http://localhost:8000/static/app/style.css 能够返回状态码为 HTTP 200。在我的控制台里显示 "GET / HTTP/1.1" 500 27 - vpoulain
好的,现在它在本地可以工作,但在Heroku上仍然无法工作。我没有任何调试信息,真是疯狂。 - vpoulain
显示剩余8条评论
10个回答

10

DEBUG=False的情况下,以前可以工作的功能现在对我来说不起作用了。

但是,在settings.py中在MIDDLEWARE启用whitenoise的修复方法解决了这个问题。最好将其放在SecurityMiddleware的下面。

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware', # add this line
    #Other middleware...
]

根据文档,实际上需要先启用它。


这对我有用。在Heroku的Django启动说明中,他们说要包含它https://devcenter.heroku.com/articles/django-assets,但我是从他们的heroku-django-template https://github.com/heroku/heroku-django-template开始的,而那里面没有包括它。 - amchugh89

7

4
或者从仪表板中的应用程序设置中删除DISABLE_COLLECTSTATIC环境变量即可。重要的是:不要将其值更改为0,只需完全删除此环境变量。这样,您就无需编辑Procfile文件。然后,一切应该正常工作(至少对我而言是这样的!)。鸣谢:YunoJuno - nik_m
@YunoJuno 谢谢你,兄弟...我花了好几个小时来调试这个破玩意儿,而你却救了我! - Julien Séveno-Piltant

5

以下是我经常使用的方法。

settings.py

DEBUG = True

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') #this is not used
# Add static folder to STATIC_DIRS
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

urls.py

from django.conf.urls.static import static
from django.conf import settings

urlpatterns = [

] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

注意

此辅助函数仅在调试模式下工作,而且只有给定的前缀是本地路径(如/static/)而不是 URL(例如http://static.example.com/)才能工作。

此外,此辅助函数仅提供实际的 STATIC_ROOT 文件夹;它不像 django.contrib.staticfiles 那样执行静态文件发现。


3
问题在于 Heroku 中的 Python 应用程序使用内置的 Web 服务器,不能提供静态文件。
您可以使用 whitenoise 应用程序,这个工作解决方案是100%有效的。
假设您已经生成了静态文件,例如:
$ python manage.py collectstatic

接下来你需要这样做:

1) $ pip install whitenoise

2) 在你的requirements.txt中添加字符串“whitenoise==3.3.0”

3) 在settings.py文件中添加代码

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

4) 在 app/wsgi.py 中添加以下代码

from whitenoise.django import DjangoWhiteNoise
application = DjangoWhiteNoise(application)

3
不再需要修改 wsgi.py 文件。 - wowkin2

2
除了以上回答之外,也可能是您没有按照https://docs.djangoproject.com/en/2.0/howto/static-files/#deployment中所述正确指定了STATIC_ROOT
对我而言,解决方法是将以下内容添加到我的生产settings.py文件的结尾。
STATIC_ROOT = "/app/static/"

要知道您在Heroku上的静态文件夹位置,请运行以下命令。
heroku run python manage.py collectstatic

然后您将在那里看到路径被显示出来。

2

如果您的设置文件不在根目录中,而是在/projectname/文件夹中,则需要使用双重dirname来设置BASE_DIR:

settings.py

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)
STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage'
# for /static/root/favicon.ico    
WHITENOISE_ROOT = os.path.join(BASE_DIR, 'staticfiles', 'root') 

template.html

{%  load staticfiles %}
<link rel="stylesheet" href="{%  static "app/css/font.css" %}">

这个例子中的应用程序树:

annuaire
|-- /annuaire
|-- -- /settings.py
|-- /app
|-- /static/app/css/font.css

谢谢!我已经做了,但它仍然不起作用...我在主问题帖子中编辑了我的Django应用程序树。 - vpoulain
当我执行collectstatic命令时,控制台输出275 static files copied to '/app/staticfiles', 1 unmodified, 276 post-processed.,但是在Heroku的bash中,staticfiles目录仍然是空的,感觉很奇怪... - vpoulain
@vpoulain 这是因为每个新的Heroku进程都在自己的临时文件系统中运行,所以新文件不会被任何其他进程看到。(请参见https://devcenter.heroku.com/articles/dynos#ephemeral-filesystem)。必须在初始推送期间运行`collectstatic`才能使文件持久化。 - D. Evans
@vpoulain,请检查应用程序树,对于您的代码,我认为您需要使用STATICFILES_DIRS = ( os.path.join(BASE_DIR,'app','static'), )或将静态文件夹移动到根目录。 - Meska
@Meska 这有点令人困惑,staticfilesstatic 应该在哪里写? - Talha Anwar

1

$ pip install whitenoise

在中间件中添加whitenoise。

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    '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',
    'whitenoise.middleware.WhiteNoiseMiddleware',
]

还需要在setting.py中添加STATIC_ROOT:

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

并在urls.py中添加media_root。

from django.conf import settings

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \
        + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

然后你可以在本地或服务器上运行collectstatic命令 python manage.py collectstatic

它会正常工作。你可以在本地运行此命令collectstatic,它也会在服务器上工作。


1
我曾经遇到过同样的问题。找到问题最简单的方法是使用
heroku run ls staticfiles/images

如果图像在应该放置文件的目录中,这将为您提供该目录中所有文件的列表。
正如我发现的那样,这是文件扩展名的大小写问题。该文件具有扩展名 .JPG,而我在模板上使用了扩展名 .jpg 进行引用。

1
我花了几个小时才终于找出了问题所在。在我看来,主要问题是Heroku官方文档中使用的旧式中间件使用了已弃用的MIDDLEWARE_CLASSES,而不是新的MIDDLEWARE设置。

在whitenoise 4+版本中,Django的WSGI集成选项(需要编辑wsgi.py)已被移除。相反,您应该将WhiteNoise添加到settings.py中的中间件列表中,并从wsgi.py中删除对WhiteNoise的任何引用。(来自文档

以下配置非常有效:

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'

MIDDLEWARE = [
    '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',
    'django.middleware.security.SecurityMiddleware',
    # the next line of code is the one that solved my problems
    'whitenoise.middleware.WhiteNoiseMiddleware',

]

请注意以下来自文档的提示。

您可能会发现其他第三方中间件建议在中间件列表的顶部赋予它最高优先级。除非您确切了解正在发生的事情,否则应忽略此建议并始终将WhiteNoiseMiddleware置于其他中间件之上


0

这对我有用

我的 requirements.txt 文件是

whitenoise==6.2.0
static3==0.7.0
webdav4==0.9.7
Django==3.2
# others

我在settings.py中删除了STATIC_ROOT,并添加了变量STATICFILES_DIRS,我的settings.py如下:


MIDDLEWARE = [
    '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',
]

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)

STATIC_URL = 'static/'

如果你设置了 heroku config:set DISABLE_COLLECTSTATIC=1,现在应该可以正常运行了。

如果你仍然想使用 COLLECTSTATIC 功能,请手动将其添加到 Procfile 文件中:

web: python manage.py collectstatic --no-input; gunicorn my_blog.wsgi --log-file - --log-level debug

我的my_blog.wsgi文件(它是按照一篇现在找不到的文章进行配置的):

import os
from django.core.wsgi import get_wsgi_application
from dj_static import Cling

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'my_blog.settings')
application = Cling(get_wsgi_application())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接