Django:在部署时通过模板引擎渲染静态文件

Posted

技术标签:

【中文标题】Django:在部署时通过模板引擎渲染静态文件【英文标题】:Django: render staticfiles through template engine at deploy-time 【发布时间】:2014-03-06 22:36:57 【问题描述】:

我想使用 Django 模板变量渲染一些静态文件(尤其是 *.js)。我相信这是一个常见的用例,尤其是在做任何 AJAX-y 时;我不想在我的 .js 文件中硬编码 AJAX url,就像我想在我的 .html 文件中硬编码它们一样。 Buuuut 当然我不希望这些静态文件在每个客户端请求时都必须通过模板引擎运行,因为这会很慢。我指的是 URL(在编译/部署后不会更改)或静态(非数据库)模型属性之类的东西。 (我想有些用例可能会在运行时更改这些东西——毕竟这是 Python——但我认为它们并不常见)。对于一些可能的模板变量(例如模型字段),当然文件必须在客户端请求时渲染,但这不是我要说的。

那么通过模板引擎渲染我的一些静态文件,对于可能的模板变量的子集,可能与collectstatic同时呈现不是有意义吗?

据我所知,目前情况并非如此。需要明确的是,我正在寻找一种解决方案,在编译/部署时通过模板引擎渲染静态文件,以便在“客户端请求时”它们实际上是普通的旧静态文件。

这种方法可以避免这些黑客攻击:

DRY URLs in Django javascript Using the Django URL Tag in an AJAX Call

免责声明:

是的,我知道有用于 javascript 的模板引擎(小胡子、车把、原型等)。当 Django 已经有一个模板引擎时,为什么还要向堆栈添加另一个模板引擎?加上语法冲突!这似乎很愚蠢。 This 看起来需要破解它,但它很复杂并且没有完全实现。

所以:

    是否有我缺少的解决方案? 如果没有,有没有办法挂钩到 collectstatic(如 pre-collectstatic 挂钩),允许在“收集”它们之前通过模板引擎呈现某些静态文件?

编辑: 还没有回应......这是一个非常愚蠢的问题,我错过了一些明显的东西吗?如果是这样...继续告诉我...

【问题讨论】:

大声笑我来晚了,读它作为 Django 通过寺庙献祭。哈哈哈。 所有三个答案看起来都是可行的解决方案。每个人都会获得赞成票。赏金是最全面的答案,在赏金授予时也有最多的投票(嗯,1,但仍然)。谢谢大家。 【参考方案1】:

    Django 有几个框架用于同一目的:django-pipeline、django-assets 等,它们集成了不同的静态文件处理策略,配置难度不同。 我使用外部工具 - Grunt(它需要 node.js) - 在 collectstatic 之后进行资产后处理。它更容易,并且有很多 plugins 用于任何目的(源验证、css/js/图像缩小、合并、测试等)。

    可以通过覆盖post_process 方法的自定义静态文件存储来挂钩collectstatic

示例/settings.py

STATIC_ROOT = 'assets'
STATICFILES_STORAGE = 'example.storage.MyStaticFilesStorage'

example/storage.py

import os
from django.contrib.staticfiles.storage import StaticFilesStorage
from django.core.files.base import ContentFile
from django.template import Template, Context


class MyStaticFilesStorage(StaticFilesStorage):

    def post_process(self, paths, dry_run=False, **options):
        # don't even dare to process the files if we're in dry run mode
        if dry_run:
            return
        js_template_data = 'foo': 'bar'  # template variables
        js_template_extension = '.jst'
        js_extension = '.js'
        for original_name, (storage, path) in paths.items():
            processed = False
            saved_name = original_name
            original_path, original_extension = os.path.splitext(original_name)
            if original_extension == js_template_extension:
               with storage.open(path) as original_file:
                  saved_name = original_path + js_extension
                  if hasattr(original_file, 'seek'):
                      original_file.seek(0)
                  original_source = original_file.read()
                  c = Context(js_template_data)
                  saved_source = Template(original_source).render(c)
                  self.delete(saved_name)
                  self.delete(original_name)
                  self._save(saved_name, ContentFile(saved_source))
                  processed = True
            yield original_name, saved_name, processed

【讨论】:

+1 在构建/部署时做事的抱怨。使用post_process 看起来很聪明;我使用了在collectstatic 之后调用的自定义管理命令(通过 Grunt)。为了简化一点,你可以让 Django 创建一个包含所有变量的 JS 文件。用myapp.global.urls = ...(或其他)命名它们。然后在整个 JS 中导入它们。我对 LESS/SASS 做同样的事情——使用 Grunt 构建的单个文件,所有其他文件都使用这些变量,然后 Grunt 处理 CSS 编译。 看起来这些中的每一个都可以适应我的用例。很好的答案。【参考方案2】:

解决问题的一种完全不同的方法是询问您是否真的需要在 javascript 中获取这些 URL——相反,Javascript 是否可以从 HTML 中的数据属性之类的东西中获取 URL?

换句话说,你可能想要:

homepage.html

<div id="pop-up-modal">pop me up</div>

homepage.js

$("#pop-up-modal").click(function 
  $.ajax("% url 'some-class-name %") 
  ...
);

当它通常可以更直接地执行以下操作时:

homagepage.html

<div id="pop-up-modal" data-popurl="% url 'some-class-name' %">pop me up</div>

homepage.js

$("#pop-up-modal").click(function 
  $.ajax($(this).data('popurl')) 
  ...
);

【讨论】:

我认为对于您描述的用例来说,这是一种很好、干净的方法。【参考方案3】:

我认为django-medusa 会满足您的需求。

通过设置渲染器并使用基于磁盘的后端,生成静态文件将非常简单:

django-admin.py staticsitegen

【讨论】:

快速浏览后,这看起来是一个不错的方法。我认为“半静态”网站的用例与我的“半静态”静态文件非常吻合。如果我理解正确,我会将路径列表设置为 ["/semistaticviews/*",] 之类的内容,然后将输出呈现为 settings.STATICFILES_DIRS?【参考方案4】:

你没疯。我也对此感到沮丧,并发现自己为我处理的每个新 Django 项目一起破解了一些东西。我认为缺乏直接解决方案的原因是这是超级干旱的骨干。对这些东西进行硬编码并称之为一天非常容易。这个和两个最常见的用例涉及从另一种语言的代码生成一种语言的代码,这往往被视为可疑。

我最近发布了一个新的 Django 包django-render-static,它可以通用地解决这个问题。它搭载了 Django 现有的模板引擎基础设施。一个名为render_static 的管理命令应该在之前 collectstatic 运行。此命令将查找在设置中注册(或作为参数传递)的模板,并将它们呈现到磁盘上的静态文件位置。然后,它们可用于您配置的任何常规静态文件打包管道。

我敢肯定还有更多用例,但我发现的两个最常见的用例是在静态 JavaScript 中提供 reverse 实用程序,相当于 Django 和自动翻译类似于定义的 python 结构(即选择字段)进入 JavaScript。该应用程序提供了两者兼而有之的模板标签。

JavaScript url 反转代码保证在功能上等同于 Django 的reverse 函数。我不会费心把示例代码放在这里,因为it's been well documented。

【讨论】:

以上是关于Django:在部署时通过模板引擎渲染静态文件的主要内容,如果未能解决你的问题,请参考以下文章

Django框架之模板

Django-模板引擎

使用 jinja2 模板引擎渲染 django 表单

Express与传统Web应用(服务端渲染art-template模板引擎配置静态资源托管)

django之模板显示静态文件

07-Django模板