在 Django 中循环块时进行条件缓存

问题描述 投票:0回答:1

在我们的网站上,我们正在循环浏览许多网页的鹡鸰块。其中一些块包含网络表单或动态/个性化数据,因此无法缓存。我们希望为编辑者提供最大的灵活性来更新网页,而无需接触代码,同时也提供良好的性能和加载时间。

在下面的代码中,字段 page.body 是一个 wagtail 流字段,编辑者可以在其中以任意顺序添加任意数量的内容块。

这是所需的解决方案,具有简化的代码,但失败了:

{% load cache wagtailcore_tags %}
<!-- Start the page cache -->
{% cache timeout url_path %}

{% for block in page.body %}

  <!-- disable the cache before an excluded block -->
  {% if block.disable_cache %}
     {% endcache %} <!-- template engine throws an error at this "endcache" tag -->
  {% endif %}

  {% include_block block %}

  <!-- reenable the cache after an excluded block is rendered -->
  {% if block.disable_cache %}
    {% cache timeout block.uuid %} 
  {% endif %}

{% endfor %}

<!-- end the page cache -->
{% endcache %}

假设一个页面有 15 个块,中间的单个块具有不应缓存的表单。在这种情况下,我们将在 2 个缓存块组之间有 1 个渲染块,这会导致对缓存的 2 次调用。这里的问题是模板渲染器不会接受或解析条件中的缓存标记,因此该解决方案失败并出现错误,模板引擎需要一个“endif”标记,其中第一个“endcache”出现在代码中。

另一种方法是像这样单独缓存每个块,这种方法有效:

{% load cache wagtailcore_tags %}

{% for block in page.body %}

  <!-- render or cache each block individually -->
  {% if block.disable_cache %}
     {% include_block block %}
  {% else %}
    {% cache timeout block.uuid %}
      {% include_block block %}
    {% endcache %}
  {% endif %}

{% endfor %}

虽然第二种解决方案有效,但它会导致渲染 1 个块并对缓存进行 14 次调用,每个块调用一次。这显然比第一个(失败的)解决方案性能低得多。

在 django 中使用片段 cahce 和循环或条件时,有人对减少缓存调用次数有想法或经验吗?或者潜在的解决方案?

django django-templates wagtail wagtail-streamfield
1个回答
0
投票

我不明白“...这会导致 1 个块被渲染并对缓存进行 14 次调用...这显然性能要差得多。”。

如果除了一个块之外的所有块都应该被缓存,并且调用次数/往返时间是一个问题(您只想进行一次缓存查找),您可以在缓存中存储一个包含所有渲染块的字典并替换重新计算的块,但现在您将面临使用整个块集更新缓存的惩罚。

我认为这确实是一个自定义案例,因此您必须在视图代码中执行此操作,如下所示:


def _render_block(request, block, context):
    context['block'] = block
    return render(request, block_template, context=context)

def view(request, ...):
    ...
    rendered_blocks = []
    cached_blocks = cache.get(cache_key) or {}
    blocks_updated = False
    for block in blocks:
        rendered_block = cached_blocks.get(block.key)
        if not rendered_block or block.disable_cache:
            rendered_block = _render_block(request, block, context={...})
            cached_blocks[block.key] = rendered_block 
            blocks_updated = True
        rendered_blocks.append(rendered_block)
    if blocks_updated:
        cache.set(cache_key, cached_blocks)
    ...
    context['blocks'] = rendered_blocks
    return render(request, template, context)

如果这确实会产生影响,我会比较调用 14 次缓存(更简单的代码)的响应时间与存储在较大块的缓存中/从缓存中恢复的响应时间。 (这取决于块更新的概率。)

© www.soinside.com 2019 - 2024. All rights reserved.