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

发布于 2024-11-06 21:51:46 字数 945 浏览 5 评论 0 原文

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

基本上我有一个模板标签,我做了类似

将变为

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

这一切都工作正常,除了:

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

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

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

另外,我知道将版本作为查询字符串的一部分可能会导致某些代理不缓存文件而出现问题 - 因此我考虑将版本作为文件名的一部分 - 例如 javascript/c/c.123456 .js

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

I am setting far-future expires headers for my CSS/Javascript so that the browsers don't ever ask for the files again once they get cached. I also have a simple versioning mechanism so that if the files change, the clients will know.

Basically I have a template tag and I do something like

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

which will become

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

The template tag opens a file javascript/c/c.js.v where it finds the version number and appends it to the query string. The version is generated by a shell script (run manually for now, will probably add pre-commit hook) which checks whether the file has changed (using git diff).

This is all working fine, EXCEPT:

I want to implement the same kind of versioning for images as well. But images can be referenced from CSS - which is a static file (served by nginx) - so no template tag there.

What is a better approach for file versioning?

Alternatively, I am thinking about replacing the template tag with a middleware which changes all links before returning the response. That is better than the template tag, which can be mistakenly omitted. But still doesn't solve the issue of images referenced from CSS.

Also, I'm aware that having the version as part of the query string might cause trouble with certain proxies not caching the file - so I consider making the version part of the filename - for example javascript/c/c.123456.js.

Note: It looks like there is no way to solve this issue using Django (obviously - since I don't even serve the CSS through Django). But there has to be a solution, perhaps involving some nginx tricks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

没有你我更好 2024-11-13 21:51:47

样式表资源

对于样式表引用的资源,最好使用 Sass 和 Sass。罗盘。 Compass 有一个 mixin,它会自动在样式表中引用的静态资源末尾添加版本查询参数。仅当您重建样式表时,版本号才会发生变化(在本地开发时,这对于 compass watch 来说是微不足道的)。

模板资源

对于其他文件,我实际上会使用某种后拉钩子来重写 python 模块,其唯一目的是包含当前版本。

/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

您的帖子拉钩将创建:

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

其中包含最新(或以前)的 git commit 哈希:

__version__ = "0763j34bf"

然后在您的 settings.live 中使用简单的 from .version import __version__ as ApplicationVersion ,您的模板标记可以简单地使用 from settings import ApplicationVersion 将该查询参数写入为缓存破坏程序。

Stylesheet Assets

For your stylesheet referenced assets, you're much better off using Sass & Compass. Compass has a mixin that will automatically add version query parameters on the end of static assets referenced within a stylesheet. The version number only changes when you rebuild the stylesheet (which is trivial with compass watch while you develop locally).

Template Assets

For other files, I would actually use a post-pull hook of some kind that rewrites a python module whose sole purpose is to contain the current version.

/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

Your post pull hook would create :

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

Which would contain the latest (or previous) git commit hash :

__version__ = "0763j34bf"

Then with a simple from .version import __version__ as ApplicationVersion in your settings.live, your template tag can simply use from settings import ApplicationVersion to write that query parameter as teh cache buster.

你是年少的欢喜 2024-11-13 21:51:47

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

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" />

We are using this simple templatetag to generate version number based on file modification time:

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

For pre 1.3 Django there is even simpler version of this tag:

@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

Usage:

<link rel="stylesheet" href="{%  staticfile "css/style.css" %}" type="text/css" media="screen" />
橘味果▽酱 2024-11-13 21:51:47

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

看来没有更好的办法了。如果您想到任何答案,请告诉我,我会考虑将该答案标记为已接受的答案。

感谢您的评论!

Will add another step to my pre-commit script to replace all direct links with links to versioned files in the minimized CSS.

Seems there is no better way to do it. If you think of any, let me know and I'll consider marking that one as accepted answer.

Thanks for your comments!

守不住的情 2024-11-13 21:51:47

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

This might help as well: http://www.fanstatic.org/

夜空下最亮的亮点 2024-11-13 21:51:47

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

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

I think a simple solution might be:

  1. Write your css files as Django templates.
  2. Write a Django command to render your css-templates (and store them in somewhere accessible)
  3. In your deployment script call this command.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文