
本文讲解如何通过 django 视图层预处理数据,避免在模板中执行复杂逻辑(如去重、分组),从而安全、高效地实现“按分类聚合商品”的展示需求。
在 Django 中,模板(Template)不是编程环境——它不支持变量赋值(如 {% used_category = [] %})、列表操作或复杂条件判断。你遇到的 TemplateSyntaxError: Invalid block tag 正是因为 Django 模板语言禁止直接声明和修改变量,这与 Python 的语义有本质区别。
✅ 正确做法是:将数据组织逻辑移至 视图(View)层,利用 Django ORM 的能力提前完成分组与关联,再将结构化数据传递给模板。
✅ 推荐方案:使用 prefetch_related + 字典分组(视图层处理)
首先,优化模型(补充 related_name 提升可读性):
class Category(models.Model):
name = models.CharField(max_length=64) # 建议字段名语义更清晰
def __str__(self):
return self.name
class ShopItem(models.Model):
name = models.CharField(max_length=64) # 同上,避免驼峰命名(PEP 8)
price = models.IntegerField()
category = models.ForeignKey(
Category,
on_delete=models.CASCADE,
related_name='items' # 关键!便于反向查询
)
def __str__(self):
return self.name然后,在视图中组织好“分类 → 商品列表”的嵌套结构:
# views.py
from django.shortcuts import render
from .models import Category
def shop_items_by_category(request):
# 预加载每个分类下的所有商品,避免 N+1 查询
categories = Category.objects.prefetch_related('items').all()
# 可选:只取有商品的分类(更高效)
# categories = Category.objects.filter(items__isnull=False).distinct().prefetch_related('items')
return render(request, 'shop/shop_list.html', {
'categories': categories
})✅ 模板中简洁渲染(无逻辑、无赋值、高性能)
| {{ category.name }} | ||
|---|---|---|
| {{ item.name }} | ${{ item.price }} | |
⚠️ 注意事项与最佳实践
- ❌ 禁止在模板中尝试模拟 Python 列表操作(如 used_category.append(...)),Django 模板语言不支持。
- ✅ 使用 prefetch_related('items') 而非 select_related(),因为这是“一对多”关系(Category ← ShopItem)。
- ✅ 若分类数量庞大但仅需展示有商品的分类,用 Category.objects.filter(items__isnull=False).distinct() 提升效率。
- ✅ 在模型中定义 related_name 是良好实践,让 category.items.all 更直观、可读性更强。
- ✅ 如需前端排序(如按价格降序),可在视图中使用 Prefetch 自定义查询集:
from django.db.models import Prefetch categories = Category.objects.prefetch_related( Prefetch('items', queryset=ShopItem.objects.order_by('-price')) ).all()
通过将数据组织逻辑交由视图和 ORM 完成,你不仅解决了模板语法错误,还显著提升了性能、可维护性与代码可测试性——这才是 Django MVT 架构的正确打开方式。










