Django模板标签“处理顺序”

10

我正在尝试编写一组模板标签,允许您轻松地在模板文件中指定js和css文件。例如:{% requires global.css %},稍后在请求中使用{% get_required_css %}

虽然我已经将其大部分功能实现,但还存在一些问题。我们先从“时间”问题开始。

每个模板标签由两个步骤组成:调用/初始化和渲染。在调用/初始化任何渲染过程之前就会发生。为了保证在呈现{% get_required_css %}之前排队所有文件,我需要在调用/初始化过程中构建所需文件的列表。

因此,我需要将所有文件收集到每个请求的一个捆绑包中。显然,context字典是这样做的地方,但不幸的是,调用/初始化没有访问context变量的权限。

这说得通吗?有人知道如何解决这个问题(而不用采用hack-y全局request对象)吗?

另一个可能性是将它们存储在本地dict中,但仍然需要与请求相关联...可能是某种{% start_requires %}标签?但我也不知道如何使其工作。


经过简要了解“中间件”的功能,我考虑使用它来插入生成的HTML。但问题在于,更难以使各种静态压缩应用程序无缝工作。 - TieDyeFriday
如果有人感兴趣,我在Github上发布了一个应用程序,它是解决这个特定问题的相当不优雅的解决方案 - http://github.com/pappy74/django-requires_js_css。它与下面Jack的答案有点类似,但打包成了一个漂亮、整洁的应用程序。 - TieDyeFriday
1个回答

2
我想出了一种更适合你需求的方法。虽然这会给服务器带来更多的负载,但是适当的缓存可以帮助减轻大部分负担。下面我概述了一种方法,如果每个路径的CSS包含相同,那么应该可以工作。您需要创建一个单独的视图来包含所有这些文件,但是您实际上可以使用此方法优化您的CSS,使每个页面仅调用一次CSS。
import md5
class LoadCss(template.Node):
    def __init__(self, tag_name, css):
        self.css = css
        self.tag_name = tag_name
    def render(self, context):
        request = context['request']
        md5key = md5.new(request.path).hexdigest()
        if md5key not in request.session:
            request.session[md5key] = list()
        ## This assumes that this method is being called in the correct output order.
        request.session[md5key].append(self.css)
        return '<!-- Require %s -->' % self.css
def do_load_css(parser, token):
    tag_name, css = token.split_contents()
    return LoadCss(tag_name, key)
register.tag('requires', do_load_css)

class IncludeCss(template.Node):
    def __init__(self, tag_name):
        self.tag_name = tag_name
    def render(self, context):
        request = context['request']
        md5key = md5.new(request.path).hexdigest()
        return '<link rel="stylesheet" href="/path/to/css/view/%s">' % md5key
def do_include_css(parser, token):
    return IncludeCss(token)
register.tag('get_required_css', do_include_css)

views.py:

from django.conf import settings
from django.views.decorators.cache import cache_page
import os

@cache_page(60 * 15) ## 15 Minute cache.
def css_view(request, md5key):
    css_requires = request.session.get(md5key, list())
    output = list()
    for css in css_requires:
        fname = os.path.join(settings.MEDIA_ROOT, 'css', css) ## Assumes MEDIA_ROOT/css/ is where the CSS files are.
        f = open(fname, 'r')
        output.append(f.read())
    HttpResponse(''.join(output), mimetype="text/css")

这样可以让您将CSS信息存储在上下文中,然后在会话中,并从视图中提供输出(使用缓存使其更快)。当然,这将增加一些服务器开销。
如果您需要根据更多内容而不仅仅是路径来变化CSS,则可以简单地修改md5行以满足您的需求。您可以访问整个请求对象和上下文,因此几乎所有内容都应该在其中。
注意:经过第二次审查,如果浏览器在会话填充之前获取CSS,则可能会导致竞争条件。我不认为Django是这样工作的,但我现在不想查看它。

这就是我最初的实现方式,但问题在于顺序。使用这种方式,你必须确保所有的'requires'在'get_required'之前被解析。当一个包含模板标签本身需要一个css文件时,这一点尤为明显。如果你能在__init__期间添加文件,那么你就可以避免这个问题。 - TieDyeFriday
我添加了另一种可能的方法来完成这个任务,它不依赖于内部排序。虽然有点复杂,但应该可以工作。 - Jack M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接