如何在 Django 中高效组织商品与分类的层级展示

本文讲解如何通过 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. 模板中简洁渲染(无需逻辑判断!)



  {% for category in categories %}
    
    {% for item in category.items.all %}
      
    {% endfor %}
  {% endfor %}
{{ 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 架构精神:职责分离、易于测试、便于复用。