You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django实现商品按评分升降序排序 无评分商品自动置底

问题根源

你现在评分排序不生效的核心原因有两点:

  • 排序逻辑调用的是Product模型内置的rating字段,但前端实际展示的评分是get_rating()方法遍历评论实时算出来的平均值,排序依据和展示数据完全脱节
  • 没有给无评分商品设置单独的排序权重,数据库默认排序会把NULL值的无评分商品混在正常商品里,达不到无评分商品全部置底的要求
修复步骤

第一步:修改views.py的排序逻辑

首先在文件头部导入排序需要的聚合和条件表达式工具:

from django.db.models import Q, Avg, Case, When, Value, IntegerField
from django.db.models.functions import Lower

然后替换all_products视图中处理sort参数的代码块,单独为评分排序自定义逻辑,其余原有排序逻辑保持不变:

if request.GET:
    if 'sort' in request.GET:
        sortkey = request.GET['sort']
        sort = sortkey
        if sortkey == 'name':
            sortkey = 'lower_name'
            products = products.annotate(lower_name=Lower('name'))
        if sortkey == 'category':
            sortkey = 'category__name'
        # 单独处理评分排序逻辑
        if sortkey == 'rating':
            # 聚合计算每个商品的评论平均评分,无评论商品的avg_rating为None
            products = products.annotate(avg_rating=Avg('reviews__rating'))
            # 增加排序权重:有评分商品权重为1,无评分商品权重为0,保证无评分商品永远排最后
            products = products.annotate(
                has_rating=Case(
                    When(avg_rating__isnull=False, then=Value(1)),
                    default=Value(0),
                    output_field=IntegerField()
                )
            )
            direction = request.GET.get('direction')
            if direction == 'desc':
                # 评分从高到低:先按权重降序(有评分的排前面),再按平均评分降序
                products = products.order_by('-has_rating', '-avg_rating')
            else:
                # 评分从低到高:先按权重降序(有评分的排前面),再按平均评分升序
                products = products.order_by('-has_rating', 'avg_rating')
        else:
            # 价格、名称、分类等其余排序逻辑保持原有逻辑不变
            if 'direction' in request.GET:
                direction = request.GET['direction']
                if direction == 'desc':
                    sortkey = f'-{sortkey}'
            products = products.order_by(sortkey)
                
    if 'category' in request.GET:
        categories = request.GET['category'].split(',')
        products = products.filter(category__name__in=categories)
        categories = Category.objects.filter(name__in=categories)

    if 'q' in request.GET:
        query = request.GET['q']
        if not query:
            messages.error(request, "You didn't enter any search criteria!")
            return redirect(reverse('products'))
        
        queries = Q(name__icontains=query) | Q(description__icontains=query)
        products = products.filter(queries)

第二步:同步修改模板评分展示逻辑

找到products.html中显示商品评分的代码块,替换为直接使用查询阶段聚合好的avg_rating字段,避免重复计算,同时保证排序和展示的数据完全一致:

{% if product.avg_rating %}
    <small class="text-muted"><i class="fas fa-star mr-1" style=color:blue;></i>{{ product.avg_rating|floatformat:1 }} / 5</small>
{% else %}
    <small class="text-muted">No Rating</small>
{% endif %}

可选性能优化(商品量大时推荐使用)

如果站点商品数量较多,可以在用户提交评论时自动把平均评分同步更新到Product模型的rating字段,避免每次列表查询都做聚合计算:
在product_detail视图创建评论的代码后追加如下逻辑:

Review.objects.create(
    product=product,
    rating=rating,
    content=content,
    created_by=request.user
)
# 同步更新商品表的平均评分字段
avg_rating = product.reviews.aggregate(Avg('rating'))['rating__avg']
product.rating = avg_rating
product.save()

使用该方案时,评分排序部分不需要再聚合avg_rating,直接用模型自带的rating字段即可,把has_rating的判断条件改为When(rating__isnull=False, then=Value(1))就行,查询性能会提升明显。

效果验证

修改完成后测试两个排序场景:

  • 选择「评分从高到低」:5分商品最先展示,按评分依次递减排列,所有无评分商品全部在列表末尾
  • 选择「评分从低到高」:1分商品最先展示,按评分依次递增排列,所有无评分商品同样在列表末尾

内容的提问来源于stack exchange,提问作者tobias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 16:06:30