Django在DEBUG模式下无法提供STATIC_ROOT

10

我正在使用Python 3.5和Django 1.10来运行开发服务器:

./manage.py runserver 0.0.0.0:8000
在我的settings.py文件中,我有:
DEBUG       = True
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATIC_URL  = '/static/'

还需要一个app目录,其中包括一个static子目录用于存放静态文件:

proj/
    proj/
        ...
    app/
        static/
            a.txt
        ...
    static/
        b.txt

相当标准。

但是:Django在DEBUG = True时不会提供STATIC_ROOT。对于/static/a.txt,它会返回app/static/a.txt,但对于/static/b.txt,它不会返回static/b.txt

更改settings.py的内容如下:

STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

工作-但是我必须注释掉STATIC_ROOT(否则Django会抱怨它不能在STATICFILES_DIRS中)。

现在,我不能只是“使用不同的外部静态目录”,例如static2,因为我正在使用django-sass-processor,它将.sass文件编译为.css文件,并将这些.css文件放在STATIC_ROOT中(正如我所说的,不可访问)。

我尝试过的事情:

  1. 设置NGINX来提供该目录(就像在生产环境中一样)。可以工作,但肯定还有其他方法。

  2. 配置django-sass-processor.css文件写入所述的“不同的外部静态目录”,例如static2,并将其包含在STATICFILES_DIRS中。同样,可以工作,但它肯定不可能那么复杂!

  3. urls.py中手动添加静态文件URL:

if settings.DEBUG:
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

我经历了一次非常棘手的问题,所以我想分享一下,以便帮助其他人:原来Django已经默认覆盖了我的URL,并且只包括我的应用程序中的静态目录和STATICFILES_DIRS中的目录。即使我将DEBUG改为False并删除if条件,也无法解决问题,因为django.conf.urls.static.static函数会返回一个空列表。因此,我使用django.views.static.serve自己实现了处理静态文件,最终解决了问题,但不得不关闭调试并手动实现静态文件服务,这很不合理。

更新:

  • 如果您正在使用django-sass-processor并遇到类似的问题,他们在文档中提供了一种特殊的静态文件查找器解决方案,您可以像这样将其添加到settings.py中:

  • STATICFILES_FINDERS = [
        'django.contrib.staticfiles.finders.FileSystemFinder',
        'django.contrib.staticfiles.finders.AppDirectoriesFinder',
        'sass_processor.finders.CssFinder',
    ]
    

    前两个是Django的默认查找器,因此当您覆盖此配置时,应手动包含它们。

    但即使现在,对于除了 .css 文件之外的任何内容,STATIC_ROOT 实际上是唯一无法通过 /static/ 访问的静态目录,我觉得这相当奇怪。因此,我仍然希望解决(或至少理解)它。


    我已经使用Django工作了将近十年。我总是在设置静态文件时遇到麻烦。 - tutuca
    1
    我非常确定这是一个简单的优先级问题。我尝试将 static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 添加到 urlpatterns 中,并在 INSTALLED_APPS 中删除 django.contrib.staticfiles。这样,STATIC_ROOT 中的文件是可以访问的,但是 myapp/static 中的文件不可以访问。如果我将 django.contrib.staticfiles 添加到 INSTALLED_APPS 中,则相反的情况会发生。 - Dunatotatos
    1个回答

    13
    乍一看,Django不从STATIC_ROOT提供文件似乎很奇怪,但如果您考虑Django用于静态文件的工作流程,则是有意义的。 Django希望您将静态文件捆绑到应用程序旁边,并将项目级静态文件保存在单独的目录中,该目录已检入版本控制。 然后,当您运行manage.py collectstatic时,STATICFILES_FINDERS用于将所有文件收集到一个单独的目录中(不应该在版本控制中),以便可以将它们部署到AWS S3或其他位置。
    因此,静态文件查找器有两个任务:
    1. 通过路径查找文件,用于DEBUG模式下的静态文件服务 2. 列出所有可用文件,用于collectstatic
    如果您能在STATICFILES_DIRS中包括STATIC_ROOT,则collectstatic命令可能会陷入循环。 此外,Django开发人员并不打算让您保存STATIC_ROOT中的文件,因为它不应该被检入版本控制。
    然而,有时您确实需要从STATIC_ROOT提供文件。 在我的情况下,我有一个Celery任务,用于创建上传图像的缩略图并将其保存到静态文件存储中。 在生产中,它最终会节省在可以提供服务的AWS S3上。
    因此,如果您有有效的使用情况需要从STATIC_ROOT提供文件,则只需定义自己的文件查找器并将其路径添加到STATICFILES_FINDERS中。
    from django.contrib.staticfiles.finders import BaseFinder
    from django.contrib.staticfiles.storage import staticfiles_storage
    
    class StaticRootFinder(BaseFinder):
        """
        For debug mode only. Serves files from STATIC_ROOT.
        """
        def find(self, path, all=False):
            full_path = staticfiles_storage.path(path)
            if staticfiles_storage.exists(full_path):
                return [full_path] if all else full_path
            return []
    
        def list(self, ignore_patterns):
            return iter(())
    

    1
    这正是我在寻找的答案,因为它解释了在调试时为什么不应该从STATIC_ROOT提供文件,这对我来说是一个明确的加分项。我自己也得出了同样的结论,只是需要看到别人也这么说! - crowie

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