Django-查询对象列表的平均评分最有效的方法是什么?

时间:2019-02-13 19:48:46

标签: django django-models django-views

我有一个简单的产品和评分类,如下所示:

class Product(models.Model):
   name = ...
   price = ...

class Rating(models.Model):
   product = models.ForeignKey("Product", ...)
   score = models.PositiveSmallIntegerField(...)

现在,我想获取所有产品的列表以及每种产品的平均评分。简单地获取所有产品很容易:

product_list = Product.objects.all()

如果我有一个产品,并且想获得该产品的平均评分:

product = get_object_or_404(Product, pk=product_id)
ratings = Rating.objects.filter(product=product)
product_avg_rating = ratings.aggregate((Avg("score")))

但是,假设我要列出所有产品及其平均评分。听起来资源/计算量很大。那么解决此问题的最佳方法是什么?我是否应该为Product类创建一个仅返回平均值的类方法,然后再在模板中调用它:

{{ product.get_average_rating }}

或者有更好的方法吗?我不太确定该怎么做,需要一些指导。

非常感谢您!

编辑:我认为我的解释不够清楚,因此这是一个实际示例。假设我有一个查询集,该查询集由slug(这只是一个搜索字词)过滤:

products = Product.objects.filter(tags__name__in=[slug])

现在如何将平均评分添加到每个产品中,以便可以在模板中执行此操作:

{% for product in products %}
   {{ product.avg_rating_score }}
{% endfor %}

1 个答案:

答案 0 :(得分:1)

您可以在第一个查询中注释Product,这样只需一个查询即可获得平均值,例如:

product = get_object_or_404(
    Product.object.annotate(avg_score=Avg('rating__score')),
    pk=product_id
)

这将导致单个Product包含一个额外的属性(仅针对 this 特定的QuerySet!),因此,我们可以在模板中使用以下属性进行渲染:

{{ product.avg_score }}

它执行的查询是:

SELECT product.*, AVG(rating.score) AS avg_score
FROM product
LEFT OUTER JOIN rating ON product.id = rating.product_id
WHERE product.id = product_id