django “如何”系列10:如何管理静态文件
django开发者最关心的是web应用中的动态部分-视图函数和模板。但是明显,web应用还有其他需要注意的部分:静态文件(图片,css,javascript等等),那些都是渲染一个完整的页面需要的东西。
对于小项目来说,这不是一个大问题,因为你只需要把你要用到的静态文件放到服务器能访问到的地方就ok了。然而,在比较大的项目里面-尤其是那些由多个app组成的-处理不同app之间的静态文件开始变得麻烦起来。这就是django.contrib.staticfiles的价值所在:它会收集你每个应用程序里面的静态文件放到一个可以被你的服务器容易访问到的位置里面。
使用方法
基本用法
- 把你的静态文件放在放在staticfiles可以找到的地方:默认情况下,这意味着是在你的INSTALLED_APPS中的一个app的static/子目录下。你可以使用STATICFILES_DIRS定义静态文件的存放位置,同时也可以使用STATICFILES_FINDERS定义staticfiles如何找到你的静态文件
- 确保django.contrib.staticfiles在你的INSTALLED_APPS里面:静态文件的默认url是以STATIC_URL(默认是/static/)开头的
- 你可以在你的模板中这样引用静态文件
<img src="{{ STATIC_URL }}images/hi.jpg" />
简单的部署静态文件
当你准备好离开本地开发环境然后部署你的项目的时候:
- 设置STATIC_URL为你的公共的静态文件的url(虽然默认的/static就已经挺ok的)
- 设置STATIC_ROOT为你的静态文件被收集的绝对路径(当你使用collectstatic管理命令的时候会用到)
STATIC_ROOT = "/home/jacob/projects/mysite.com/sitestatic"
- 运行collectstatic管理命令。这会遍历你的静态文件的存储路径并把所有的静态文件拷贝到STATIC_ROOT指定的目录下。
./manage.py collectstatic
- 通过配置你选择的web服务器的STATIC_ROOT和STATIC_URL来部署和服务静态文件。
在模板中引用静态文件
有时候你需要在你的模板文件中引用静态文件,你可以像下面这样把一个url硬编码在模板中
<img src="http://static.example.com/static/myimage.jpg" />
很显然,这不是一个很好的做法,因为当你改变了静态文件的存放位置的时候你需要逐一的去修改代码,这将是一个毁灭性的工作,并且也不利于部署。
更好的方法应该是使用STATIC_URL,就像上面的例子一样,这意味着你需要该一个值:STATIC_URL,这将是你工作大大减少。
django提供了几个内建的方法在模板中使用这个配置值:上下文处理器和模板标签
在上下文处理器中使用
自带的上下文处理器是非常简单的方法,只要你保证django.core.context_processors.static在你的TEMPLATE_CONTEXT_PROCESSORS里面,默认是已经包含的啦
一旦确保在里面,你就可以在你的模板中使用了。
<img src="{{ STATIC_URL }}images/hi.jpg" />
如果{{STATIC_URL}}没有生效,请检查你是否使用了RequestContext(要求使用RequestContext)
在模板标签中使用
直接看例子吧
{% load staticfiles %} <img src="{% static "images/hi.jpg" %}" /> <!--可以接受模板上下文变量---> {% load staticfiles %} <link rel="stylesheet" href="{% static user_stylesheet %}" type="text/css" media="screen" />
在开发中服务静态文件
静态文件工具总是想帮助成功的把静态文件部署进产品环境里面,这通常意味着一个独立的专用的静态文件服务器(在本地开发时是一个很大甚至很烦的开销)。因此,staticfiles应用自带了一个快速但复杂的帮助视图函数去帮助你在开发中在本地服务静态文件。
当你使用内建的runserver命令时,这个视图函数自动启用并且在你使用STATIC_URL的时候服务你的静态文件。
如果你想使用其他的服务器进行本地开发的时候,要想启用这个视图函数,你可以在你的urlConf文件里面添加几行代码(第一行保证在urlconf的顶部,最后一行在urlconf的底部)
from django.contrib.staticfiles.urls import staticfiles_urlpatterns #你剩下的urlconf配置 urlpatterns += staticfiles_urlpatterns()
注意:
- 这仅仅在DEBUG=True是生效,因为这个视图函数是非常低效和可能不安全的,应该只用于开发而不能用于产品
- 另外,当使用staticfiles_urlpatterns时,你的STATIC_URL不能为空串或者一个完整的url,比如http://static.example.com/
服务其他目录
serve(request, path, document_root, show_indexes=False)
这个视图函数可以服务任何的给定的目录(再次重复,仅仅用于开发环节,不要用于产品)。最有可能的例子可能是MEDIA_ROOT中用户上传的内容,因为staticfiles是为静态文件服务的因而没有内建的处理用户上传内容的方法,不过你可以像下面这样做(前提是保证你的MEDIA_URL的值是/media/)
from django.conf import settings # ... the rest of your URLconf goes here ... if settings.DEBUG: urlpatterns += patterns('', url(r'^media/(?P<path>.*)$', 'django.views.static.serve', { 'document_root': settings.MEDIA_ROOT, }), )
如果你觉得定义一个url patterns比较烦,那么你可以试一下这个函数static,这个函数接受一个前缀(比如 MEDIA_URL)和一个点号路径的视图函数,以及其他的关键字参数
from django.conf import settings from django.conf.urls.static import static urlpatterns = patterns('', # ... the rest of your URLconf goes here ... ) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
在产品中服务静态文件
把静态文件放到产品环境中的基本轮廓是简单的:当静态文件发生变化时运行collectstatic,然后安排收集的静态文件目录(STATIC_ROOT)移到静态文件服务器然后提供服务。
当然,和所有的部署任务一样,细节是魔鬼。每一个产品安装都会有不一样的地方,因此你需要根据你的需要去调整的基本框架,下面是一个常见的可能会有用的部分。
为来自同一个服务器的app和静态文件提供服务
- 把你的代码放到部署服务器上
- 在服务器上运行collectstatic命令把所有的静态文件拷贝到STATIC_ROOT指向的目录
- 为你的服务器指明STATIC_ROOT的位置(可以参照如何部署django那节内容)
你可能想自动化完成这个过程,特别是你有多个服务器的时候。有很多方法可以完成这个自动化,但django开发者比较享受的是Fabric,如果想了解的话,自己去看Fabric的文档吧,下面是一个例子:
from fabric.api import * # Hosts to deploy onto env.hosts = ['www1.example.com', 'www2.example.com'] # Where your project code lives on the server env.project_root = '/home/www/myproject' def deploy_static(): with cd(env.project_root): run('./manage.py collectstatic -v0 --noinput')
在专用的服务器上为静态文件提供服务
大部分的比较大的django应用都会使用独立的服务器-没有运行django-只是为静态文件提供服务的。这个服务器通常运行着不同类型的web服务器-更快但功能更集中。比如:
- lighttpd
- nginx
- tux
- Cherokee
- Apache的简装版
如何配置这些服务器超出了本节的范围,你可以自己去下载和查看他们的使用文档。
由于这些静态文件服务器可能没有运行django,所以你需要修改部署策略使得看起来是这样的:
当你的静态文件改变的时候,在本地运行collectstatic
把你的本地的STATIC_ROOT目录放到静态文件服务器提供服务的那个目录下。rsync(rsync是类unix系统下的数据镜像备份工具)是这个步骤的一个好选择,因为它仅仅需要改变哪些已经发生变化的静态文件的位。
from fabric.api import * from fabric.contrib import project # Where the static files get collected locally env.local_static_root = '/tmp/static' # Where the static files should go remotely env.remote_static_root = '/home/www/static.example.com' @roles('static') def deploy_static(): local('./manage.py collectstatic') project.rysnc_project( remote_dir = env.remote_static_root, local_dir = env.local_static_root, delete = True )