如何在Django首页按商品分类分组逐行展示对应商品
问题根源
原有代码直接遍历全量商品,每读取一个商品就渲染一次分类标题,会导致同一分类标题重复出现、同分类商品被拆分到多个标题下,无法实现分类聚合展示的效果。正确逻辑是先遍历所有商品分类,再在每个分类区块内渲染该分类关联的全部商品。
具体实现方案
1. 修改视图层逻辑
不需要直接把全量商品传入模板,改为查询所有分类,同时通过预加载关联数据的方式避免数据库N+1查询问题,性能更好。
替换views.py中home视图的代码:
from .models import Category, Products def home(request): # 查询全部分类,同时预加载每个分类下的关联商品 categories = Category.objects.prefetch_related("categoty_related_name").all() context = { "categories": categories } return render(request, 'home.html', context)
说明:你之前在context_processors中已经注入了categories变量,但视图层传递的同名变量会覆盖它,直接在视图中做预加载处理可以减少不必要的数据库查询,性能更优。
2. 模型层说明
你的现有模型不需要修改,仅需注意:你在Products模型的外键中配置了related_name="categoty_related_name"(存在拼写失误,正确拼写应为category,但不影响功能运行),后续代码统一使用你已经定义的这个关联名即可,每个Category实例都可以通过.categoty_related_name.all()获取该分类下的所有商品。
3. 修改模板层代码
将原有遍历全量商品的单层循环,改为「外层遍历分类、内层遍历分类下商品」的两层嵌套循环。
替换模板中原有循环部分的代码:
{% for category in categories %} <!-- 分类标题区块 --> <h4 class="text-start montserrat_alternates_font">{{ category.title }}</h4> <hr> <!-- 遍历渲染当前分类下的所有商品 --> {% for product in category.categoty_related_name.all %} <!--- product card ---> <div class="col mb-5"> <div class="card h-100"> <!-- Sale badge--> {% if product.offer_price != None %} <div class="badge bg-dark text-white position-absolute" style="top: 0.5rem; right: 0.5rem"> SALE </div> {% endif %} <!-- Product image--> <img class="card-img-top" style="height:150px;" src="{{product.product_image.url}}" alt="商品图片" /> <!-- Product details--> <div class="card-body "> <div class="text-center"> <!-- Product name--> <h5 class="fw-bolder product_title">{{product.product_title}}</h5> <!-- 可自行补充商品价格、折扣价等字段的渲染逻辑 --> </div> </div> </div> </div> {% endfor %} <!-- 单个分类商品渲染完成,自动进入下一个分类的循环 --> {% endfor %}
可选优化
- 如果需要隐藏没有任何商品的空分类,可以修改视图中的分类查询逻辑:
categories = Category.objects.prefetch_related("categoty_related_name").filter(categoty_related_name__isnull=False).distinct() - 如果需要对分类下的商品排序(比如按创建时间倒序),可以在预加载时指定排序规则:
from django.db.models import Prefetch categories = Category.objects.prefetch_related( Prefetch("categoty_related_name", queryset=Products.objects.order_by("-created_date")) ).all()
内容的提问来源于stack exchange,提问作者Mossaddak
相关产品推荐
相关产品推荐

