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动态加载非缓存块:
实现思路:
- 模板渲染时,给非缓存块留占位符:
{% 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 %} - 页面加载后,通过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); }); }); - 后端编写API视图,专门返回非缓存块的渲染内容。
这种方式只需要一次页面级缓存调用,动态块单独请求,适合动态块占比极低的场景。
内容的提问来源于stack exchange,提问作者Jason Sage
相关产品推荐
相关产品推荐

