在 django 管理表单中将查询字符串参数添加到媒体文件

Cha*_*e G 5 git django django-admin

我想将版本号附加到 django 管理表单的 Media 类中定义的 javascript 文件。

  class Media:    
        js = ['scripts/my_file.js?v=abc1234, ]
Run Code Online (Sandbox Code Playgroud)

但是...... url 被编码,以便“?” 变为“%3F”。

有没有办法让 django 不对 URL 进行编码?

附带说明一下,我这样做的原因是通过将以下内容放入我的设置文件中,在站点加载时获取静态文件的最后一次 git 提交:

STATIC_COMMIT = check_output(['git', 'log','-n', '1','--pretty=format:%h','--', os.path.join(BASE_DIR, 'my_app/static')]).decode("utf-8").strip()
Run Code Online (Sandbox Code Playgroud)

然后在 Media 类中使用以下内容:

js = [f'scripts/my_file.js?{settings.STATIC_COMMIT}', ]
Run Code Online (Sandbox Code Playgroud)

如果我能做到这一点,那么我将永远不必手动对媒体文件进行版本控制或要求用户刷新浏览器缓存,或者最糟糕的是从缓存运行错误的 javascript(或 css)文件。

Cha*_*e G 2

好的,正如我所想,js 媒体列表都归结为使用 django.forms.widget 函数填充模板

    def render_js(self):
    return [
        format_html(
            '<script type="text/javascript" src="{}"></script>',
            self.absolute_path(path)
        ) for path in self._js
    ]
Run Code Online (Sandbox Code Playgroud)

在工作的深处absolute_path是对函数的调用django.templatetags.static.static,该函数执行破坏“?”的编码 来标记查询字符串。

因此,我VersionedMediaJS使用自定义render_js方法创建了一个类,它不涉及复杂的内容,只是附加版本:

class VersionedMediaJS():
    def __init__(self, path, version):
        self.path = forms.widgets.Media.absolute_path(None, path)
        self.version = version

    def render(self):
        html = '<script type="text/javascript" src="{0}?v={1}"></script>'
        return format_html(html, mark_safe(self.path), self.version)

    @staticmethod
    def render_js(media_object):
        html = []
        for path in media_object._js:
            if hasattr(path, 'version'):
                html.append(path.render())
            else:
                html.append(format_html('<script type="text/javascript" src="{0}"></script>', media_object.absolute_path(path)))
        return html


forms.widgets.Media.render_js = VersionedMediaJS.render_js
Run Code Online (Sandbox Code Playgroud)

VersionedMediaJS()在我的表单 Media 类中,除了纯字符串文件之外,我现在还可以传递一个对象,如下所示:

class Media():
    from django.conf import settings
    js = ["scripts/whatever.js",
          VersionedMediaJS('scripts/important.js',settings.SOURCE_COMMIT_STATIC_FILES_REV),
          VersionedMediaJS('scripts/another.js','1.2')]
Run Code Online (Sandbox Code Playgroud)

结果将如下插入到模板中:

<script type="text/javascript" src="/static/scripts/whatever.js"></script>
<script type="text/javascript" src="/static/scripts/important.js?v=50cdbe0"></script>
<script type="text/javascript" src="/static/scripts/another.js?v=1.2"></script>
Run Code Online (Sandbox Code Playgroud)

由于我通过 git commit 驱动版本号,因此每当提交新的 javascript 文件(或更准确地说拉到我的产品服务器)时,版本都会自动更改。

我可以对 css 文件做同样的事情,但我在这里没有这样做。

特别感谢:dmpayton/django-embedded-media,它在直接解决嵌入脚本和 CSS 而不是版本控制的同时提供了灵感。