使用Django进行静态文件版本控制

时间:2011-05-12 10:27:56

标签: html django nginx web-optimization

我正在为我的CSS / Javascript设置远期过期标题,以便浏览器在缓存后不会再次请求文件。我还有一个简单的版本控制机制,这样如果文件发生变化,客户端就会知道。

基本上我有一个模板标签,我做的事情就像

<script type="text/javascript" src="{{ MEDIA_URL }}{% versioned "javascript/c/c.js" %}"></script>

将成为

<script type="text/javascript" src="http://x.com/media/javascript/c/c.min.js?123456"></script>

模板标记打开文件javascript/c/c.js.v,在其中找到版本号并将其附加到查询字符串。该版本由shell脚本生成(现在手动运行,可能会添加预提交挂钩),用于检查文件是否已更改(使用git diff)。

这一切都正常,除了:

我也希望为图像实现相同类型的版本控制。但是图像可以从CSS引用 - 这是一个静态文件(由nginx提供) - 因此没有模板标记。

文件版本控制的更好方法是什么?

或者,我正在考虑用中间件替换模板标签,中间件在返回响应之前更改所有链接。这比模板标签更好,模板标签可能会被错误地省略。但仍然无法解决CSS引用的图像问题。

另外,我知道将版本作为查询字符串的一部分可能会导致某些代理没有缓存文件的麻烦 - 所以我考虑将版本作为文件名的一部分 - 例如javascript/c/c.123456.js。< / p>

注意:看起来没有办法使用Django解决这个问题(显然 - 因为我甚至没有通过Django提供CSS)。但必须有一个解决方案,可能涉及一些nginx技巧。

5 个答案:

答案 0 :(得分:1)

样式表资产

对于样式表引用的资产,使用Sass&amp;罗盘。 Compass有一个mixin,它会在样式表中引用的静态资产的末尾自动添加版本查询参数。版本号仅在重建样式表时更改(在本地开发时,compass watch对于/var/www/aweso.me/ ./files/ ./private-files/ ./static/ ./project/ ./manage.py ./fabfile.py ./.gitignore ./base/ ./__init__.py ./wsgi.py ./settings/ ./__init__.py ./modules ./__init__.py ./users.py ./email.py ./beta.py ./redis.py ./haystack.py ./version.py ./default.py ./local.py ./live.py 非常简单。)

模板资产

对于其他文件,我实际上会使用某种类型的post-pull挂钩重写一个python模块,其唯一目的是包含当前版本。

/var/www/aweso.me/project/base/settings/version.py

您的帖子拉钩会创建:

__version__ = "0763j34bf"

哪个包含最新的(或以前的)git commit hash:

from .version import __version__ as ApplicationVersion

然后在settings.live中使用简单的from settings import ApplicationVersion,您的模板标记可以简单地使用{{1}}将该查询参数写为缓存区。

答案 1 :(得分:0)

我们正在使用这个简单的模板标签根据文件修改时间生成版本号:

import os
import posixpath
import stat
import urllib

from django import template
from django.conf import settings
from django.contrib.staticfiles import finders

register = template.Library()

@register.simple_tag
def staticfile(path):
    normalized_path = posixpath.normpath(urllib.unquote(path)).lstrip('/')
    absolute_path = finders.find(normalized_path)
    if not absolute_path and getattr(settings, 'STATIC_ROOT', None):
        absolute_path = os.path.join(settings.STATIC_ROOT, path)
    if absolute_path:
        return '%s%s?v=%s' % (settings.STATIC_URL, path, os.stat(absolute_path)[stat.ST_MTIME])
    return path

对于1.3之前的Django,这个标签的版本更简单:

@register.simple_tag
def staticfile(path):
    file_path = os.path.join(settings.MEDIA_ROOT, path)
    url = '%s%s?v=%s' % (settings.MEDIA_URL, path, os.stat(file_path)[stat.ST_MTIME])
    return url

用法:

<link rel="stylesheet" href="{%  staticfile "css/style.css" %}" type="text/css" media="screen" />

答案 2 :(得分:0)

将在我的预提交脚本中添加另一个步骤,用最小化CSS中的版本化文件链接替换所有直接链接。

似乎没有更好的方法来做到这一点。如果您有任何想法,请告诉我,我会考虑将其标记为已接受的答案。

感谢您的评论!

答案 3 :(得分:-1)

这也可能有所帮助:http://www.fanstatic.org/

答案 4 :(得分:-1)

我认为一个简单的解决方案可能是:

  1. 将您的css文件写为Django模板。
  2. 编写一个Django命令来渲染你的css模板(并将它们存储在可访问的地方)
  3. 在部署脚本中调用此命令。