
本文讲解如何通过 django 的视图层预处理数据,将商品按分类分组并传递给模板,从而避免在模板中使用非法语法操作变量(如定义列表、条件去重等),实现清晰、可维护的分类商品表格展示。
在 Django 模板中,无法执行赋值操作(如 {% used_category = [] %})或运行复杂逻辑(如动态列表追加、对象比较判断)——这正是你遇到 TemplateSyntaxError: Invalid block tag 的根本原因。Django 模板语言是故意限制逻辑能力的设计,其核心原则是:视图负责数据准备与业务逻辑,模板仅负责展示。
✅ 正确做法是:在视图中完成「按分类聚合商品」的工作,并将结构化数据传入模板。
1. 优化模型(推荐添加 related_name)
首先,为 ForeignKey 显式指定 related_name,便于反向查询:
class ShopItem(models.Model):
itemName = models.CharField(max_length=64)
price = models.IntegerField()
category = models.ForeignKey(
Category,
on_delete=models.CASCADE,
related_name='items' # ← 关键:允许 category.items.all() 获取所有商品
)2. 在视图中组织分组数据
使用 prefetch_related 提升性能,并按分类聚合:
# views.py
from django.shortcuts import render
from .models import Category
def shop_items_by_category(request):
# 获取所有有商品的分类,并预加载其关联商品
categories = Category.objects.prefetch_related('items').annotate(
item_count=models.Count('items')
).filter(item_count__gt=0).order_by('category')
return render(request, 'shop/items_by_category.html', {
'categories': categories
})? prefetch_related('items') 会一次性查出所有分类及其商品,避免 N+1 查询;filter(item_count__gt=0) 确保只显示有商品的分类。
3. 模板中简洁渲染(无需逻辑判断!)
| {{ category.category }} | ||
|---|---|---|
| {{ item.itemName }} | {{ item.price }} ¥ | |
⚠️ 注意事项
- 禁止在模板中尝试定义变量或修改状态:Django 模板不是 Python 脚本,不支持 = 赋值、append()、in 列表查找等操作。
- 避免在模板中做重复过滤:如原代码中对 shopitem_list 多次遍历比对 item.category,既低效又易错。
- 善用 related_name + prefetch_related:这是 Django 推荐的关联数据获取方式,兼顾性能与可读性。
- 若需更灵活分组(如空分类也显示),可用 Category.objects.all().prefetch_related('items'),模板中用 {% if category.items.all %} 判断即可。
通过将数据组织逻辑移至视图层,你不仅解决了语法错误,还让代码更符合 Django 的 MVT 架构精神:职责分离、易于测试、便于复用。










