You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django循环Wagtail Blocks时的条件缓存优化方案求助

解决Django+Wagtail StreamField块分段缓存的性能优化方案

针对你遇到的「Django模板无法在条件内使用cache标签、单个块缓存调用次数过多」的问题,以下是几个落地性强的优化思路:

1. 预分组可缓存/非缓存块(推荐)

在模板渲染前,把page.body的StreamField块按「连续可缓存块」和「非缓存块」分组,将连续的可缓存块合并为一个缓存段,大幅减少缓存调用次数。

实现步骤:

  • 自定义模板标签,对StreamField的内容进行分组处理:

    # templatetags/wagtail_cache_tags.py
    from django import template
    from wagtail.core.models import Page
    
    register = template.Library()
    
    @register.simple_tag
    def get_cached_segments(stream_value):
        segments = []
        current_cached_blocks = []
        # 定义不可缓存的块类型(根据你的实际情况调整)
        UNCACHABLE_TYPES = ['contact_form', 'user_profile']
    
        for block in stream_value:
            if block.block_type not in UNCACHABLE_TYPES:
                current_cached_blocks.append(block)
            else:
                # 先处理之前积累的可缓存块
                if current_cached_blocks:
                    segments.append({
                        'type': 'cached',
                        'blocks': current_cached_blocks,
                        # 生成唯一缓存键:页面ID+块顺序+内容哈希
                        'cache_key': f"block-group-{stream_value.instance.id}-{len(segments)}-{hash(tuple(b.id for b in current_cached_blocks))}"
                    })
                    current_cached_blocks = []
                # 添加非缓存块
                segments.append({'type': 'uncached', 'block': block})
        # 处理最后一组可缓存块
        if current_cached_blocks:
            segments.append({
                'type': 'cached',
                'blocks': current_cached_blocks,
                'cache_key': f"block-group-{stream_value.instance.id}-{len(segments)}-{hash(tuple(b.id for b in current_cached_blocks))}"
            })
        return segments
    
  • 在模板中使用分组结果渲染:

    {% load cache wagtail_cache_tags %}
    {% get_cached_segments page.body as segments %}
    
    {% for segment in segments %}
        {% if segment.type == 'cached' %}
            {% cache 3600 segment.cache_key %}
                {% for block in segment.blocks %}
                    {% include block.template %}
                {% endfor %}
            {% endcache %}
        {% else %}
            {% include segment.block.template %}
        {% endif %}
    {% endfor %}
    

    这种方式能把连续的可缓存块合并为一次缓存调用,原本14次的调用可能会压缩到2-3次。

2. 给可缓存块添加内置缓存逻辑

让可缓存的Block类自己处理缓存,模板中无需手动写cache标签,实现逻辑内聚。

实现示例:

# blocks/base.py
from django.core.cache import cache
from wagtail.core.blocks import StructBlock, RichTextBlock

class CachedBlockMixin:
    cache_timeout = 3600

    def render(self, value, context=None):
        if not context:
            return super().render(value, context)
        page = context.get('page')
        if not page:
            return super().render(value, context)
        
        # 生成唯一缓存键
        cache_key = f"block-{self.name}-{page.id}-{hash(value)}"
        cached_content = cache.get(cache_key)
        if cached_content:
            return cached_content
        
        content = super().render(value, context)
        cache.set(cache_key, content, self.cache_timeout)
        return content

# 给需要缓存的块添加Mixin
class CachedRichTextBlock(CachedBlockMixin, RichTextBlock):
    pass

之后编辑者添加的CachedRichTextBlock会自动缓存渲染结果,模板中只需正常循环page.body即可,无需额外处理。

3. 页面级缓存+动态块AJAX替换

如果页面中动态/个性化块数量极少,可先缓存整个页面,再用JS动态加载非缓存块:

实现思路:

  1. 模板渲染时,给非缓存块留占位符:
    {% for block in page.body %}
        {% if block.block_type in uncachable_block_types %}
            <div class="uncached-block" data-block-id="{{ block.id }}" data-page-id="{{ page.id }}"></div>
        {% else %}
            {% include block.template %}
        {% endif %}
    {% endfor %}
    
  2. 页面加载后,通过AJAX请求获取非缓存块的内容并填充:
    document.addEventListener('DOMContentLoaded', function() {
        document.querySelectorAll('.uncached-block').forEach(el => {
            const pageId = el.dataset.pageId;
            const blockId = el.dataset.blockId;
            fetch(`/api/blocks/${pageId}/${blockId}/`)
                .then(res => res.text())
                .then(html => el.innerHTML = html);
        });
    });
    
  3. 后端编写API视图,专门返回非缓存块的渲染内容。

这种方式只需要一次页面级缓存调用,动态块单独请求,适合动态块占比极低的场景。

内容的提问来源于stack exchange,提问作者Jason Sage

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:27:21