我正在尝试执行类似于此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个查询并循环查看每个查询的结果。
有没有更好的方法可以在单个查询中编码,或者至少避免在每个查询集中循环?
任何帮助都非常感激。
答案 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比循环每个列表并逐个添加元素
更快此外,在调用列表时会对查询集进行评估(因为在此之前它们不会访问数据库)
仅供参考,以上将在评估时达到数据库两次。