通过评级数量或评级来排序对象

时间:2015-07-24 14:01:51

标签: python django django-models

我正在尝试执行类似于此SO问题中的第一个响应的内容:SQL ordering by rating/votes,其中可以对资源进行评级(每个资源每个用户一个评级),但在根据资源评级排序资源时,任何少于X个单独评级的资源都会出现在X或以上的资源之下。

我在Django中实现了这一点,我更倾向于避免使用原始查询并保留在Django模型和查询框架中。

到目前为止,这就是我所拥有的:

data = []
data_top = Resource.objects.all().annotate(rating=Avg('resourcerating__rating'),rate_count=Count('resourcerating')).exclude(rate_count__lt=settings.ORB_RESOURCE_MIN_RATINGS).order_by(order_by)
for d in data_top:
    data.append(d)

data_bottom = Resource.objects.all().annotate(rating=Avg('resourcerating__rating'),rate_count=Count('resourcerating')).exclude(rate_count__gte=settings.ORB_RESOURCE_MIN_RATINGS).order_by(order_by)
for d in data_bottom:
    data.append(d)

这一切都按照我的需要按功能排序并返回排序,但是,它感觉效率不高 - 运行2个查询并循环查看每个查询的结果。

有没有更好的方法可以在单个查询中编码,或者至少避免在每个查询集中循环?

任何帮助都非常感激。

2 个答案:

答案 0 :(得分:1)

您目前正在查询两次并迭代两次,但您可以将其简化为一次,只需查询按评级排序的项目,然后按以下方式迭代:

data_top = []
data_bottom = []
data = Resource.objects.all().annotate(rating=Avg('resourcerating__rating'),rate_count=Count('resourcerating')).order_by(order_by)
for d in data:
    if data.rate_count >= settings.ORB_RESOURCE_MIN_RATINGS:
        data_top.append(d)
    else:
        data_bottom.append(d)

data = data_top + data_bottom 

这也可以仅通过查询来完成,方法是创建另一个包含值rate_count < settings.ORB_RESOURCE_MIN_RATINGS的聚合列(对于高于或高于阈值的值返回0,对于下面的值返回1)和排序(new_column,rating) 。很确定这需要一些自定义SQL,但也许其他人不知道。

答案 1 :(得分:1)

from itertools import chain

main_query = Resource.objects.all().annotate(rating=Avg('resourcerating__rating'),rate_count=Count('resourcerating'))

data_top_query = main_query.exclude(rate_count__lt=settings.ORB_RESOURCE_MIN_RATINGS).order_by(order_by)
data_bottom_query = main_query.exclude(rate_count__gte=settings.ORB_RESOURCE_MIN_RATINGS).order_by(order_by)

data = list(chain(data_top_query, data_bottom_query))

使用itertools.chain比循环每个列表并逐个添加元素

更快

此外,在调用列表时会对查询集进行评估(因为在此之前它们不会访问数据库)

仅供参考,以上将在评估时达到数据库两次。