解决uWSGI里的Django静态文件丢失

uWSGI来单独运行一个Django应用,有一个static文件丢失的大坑。 本文介绍如何出坑。

(本文针对的Django版本在1.4以上,因为1.3以前有ADMIN_MEDIA_PREFIX这个坑,本文并不涉及。)

现象

一个Django应用,在./manage.py runserver情况下,正常运行。 而在uWSGI运行的情况下,则网页样式奇怪,静态文件找不到。

也就是说,调试的情况下没问题,生产环境找不到静态文件。

(假如在runserver的调试环境下也有问题,则通常是配置有误。 本文不讨论配置错误的情况。)

以下举例说明。

settings.py配置中,使用了django.contrib.staticfiles,并且做出以下配置。

BASE_DIR = os.path.dirname(os.path.realpath(__file__))

STATIC_ROOT = '/srv/django/static'
STATIC_URL = '/static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

现象为,STATIC_URL位置的文件,在uWSGI下,无法用浏览器访问,而在runserver下则没问题。

原因

一个Django应用,一般有两类静态文件。 一是应用内的静态文件,二是Django自带的。

按照前面代码的示例,应用内的静态文件在与settings.py同级的static目录下。

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

此外,在INSTALLED_APPS中配置了django.contrib.admin, 则还会有另外一组静态文件,在Django安装位置里。

例如,一个root在Python 3.6版本安装的Django,admin的静态文件在: /usr/local/lib/python3.6/site-packages/django/contrib/admin/static/admin/

最终,在STATIC_URL里,会有两类静态文件。 /static/*/static/admin/*

了解原理,原因就很显然了。 uWSGI根本不知道静态文件在什么位置。

解决

STATIC_ROOT这个参数,在runserver情况下是无用的,因为Django自己知道位置在哪。

在集成了django.contrib.staticfiles后,./manage.py多了一个子命令。

./manage.py collectstatic

这个子命令,会把所有STATICFILES_DIRS目录下的文件,都复制到STATIC_ROOT中。 在这里,也就是/srv/django/static目录下。

uWSGI也是支持静态文件的,可以通过执行时添加参数:

uwsgi --static-map /static=/srv/django/static

或者,在配置文件中添加:

static-map = /static=/srv/django/static

=前面的/staticuWSGI的URL前缀,而后面的/srv/django/static则是静态文件的路径。 这个路径,通常使用绝对路径,但也支持相对路径。

所以,生产环境部署时,解决方案一共分两步:

  1. 使用collectstatic子命令,收集、复制相关静态文件。
  2. 启动uWSGI,其中包含static-map配置、或--static-map参数。

另外,如果是使用Nginx代理,也可以考虑把静态文件交给它。 这里不做介绍。

参考


相关笔记