2016-11-23 54 views
1

我试图创建一个查询,并根据权重的自定义计算对其进行排序。Django加权查询(注释值)

我需要一些帮助,因为该解决方案我来确实工作,但性能并不在那里,我想它是

我已经是一个媒体对象。它有相关的评论,喜欢和订单。

什么当前工作,但一个完整的哈克烂摊子以下查询:

products = Media.objects \ 
     .select_related(
      'image', 
      'currency', 
      'user', 
      'user__image', 
     ) \ 
     .prefetch_related('category', 'tags') \ 
     .exclude(is_deleted=1) \ 
     .filter(Q(category__category__in=categories) | Q(tags__tag__title=query)) \ 
     .annotate(order_count = Count('orders', distinct=True)) \ 
     .annotate(comment_count = Count('comments', distinct=True)) \ 
     .annotate(like_count = Count('likes', distinct=True)) \ 
     .annotate(weight = Count(0)) \ 
     .distinct() 

    for m in products.iterator(): 
     initial_weight = int(m.order_count)*40 + int(m.comment_count)*4 + int(m.like_count)*4 + int(m.clicks) 
     m.weight  = float(float(initial_weight) - float(m.views/50)) 

正如你看到的,我单独注释我将使用的所有参数,然后做一个愚蠢的迭代全算术针对查询集中每个项目的操作都非常次优。

有一件事我试图做的是以下几点:在注释

products = Media.objects \ 
     .select_related(
      'image', 
      'currency', 
      'user', 
      'user__image', 
     ) \ 
     .prefetch_related('category', 'tags') \ 
     .exclude(is_deleted=1) \ 
     .filter(Q(category__category__in=categories) | Q(tags__tag__title=query)) \ 
     .annotate(weight = Count('orders', distinct=True) * 40 + Count('comments', distinct=True) * 4 + Count('likes', distinct=True) - F('views')/50 + F('clicks')) 

但类似操作,不可能(试图与不求和()有一些变化 - Django的总是抱怨注解值是不同的型。

通过我们使用Django 1.8本项目的方式。

是否有一个良好的单一查询的形式给出,这是让我所需的排序权重?

编辑:

我的最终解决方案(感谢Назар)

media_list = Media.objects \ 
     .select_related('user', 'user__image', 'image', 'currency') \ 
     .filter(user__in=suggested_users) \ 
     .filter(created_at__gt=seven_days_ago) \ 
     .exclude(sold=True) \ 
     .annotate(order_count = Count('orders', distinct=True)) \ 
     .annotate(comment_count = Count('comments', distinct=True)) \ 
     .annotate(like_count = Count('likes', distinct=True)) \ 
     .annotate(
      initial_weight=ExpressionWrapper((F('order_count') * 40 + F('comment_count') * 4 + \ 
          F('like_count') * 4 + F('clicks')), output_field=FloatField()) 
     ) \ 
     .annotate(
      views_divided=ExpressionWrapper(F('views')/Decimal(50.0), output_field=FloatField()) 
     ) \ 
     .annotate(weight=F('initial_weight') - F('views_divided')) \ 
     .distinct() 

回答

1

首先,你需要确保部门将产生浮动(不四舍五入)。您需要像这样(disgracefully stolen here):

ExpressionWrapper(
    (F('views')/Decimal(50.0), 
    output_field=FloatField()), 
) 

因此,查询应该是这样的:

products = Media.objects \ 
    .exclude(is_deleted=1) \ 
    .filter(Q(category__category__in=categories) | Q(tags__tag__title=query)) \ 
    .annotate(order_count = Count('orders', distinct=True)) \ 
    .annotate(comment_count = Count('comments', distinct=True)) \ 
    .annotate(like_count = Count('likes', distinct=True)) \ 
    .annotate(weight = Count(0)) \ 
    .annotate(
     initial_weight=ExpressionWrapper(
      F('order_count') * 40 + F('comment_count') * 4 + \ 
      F('like_count') * 4 + F('clicks'), 
      output_field=FloatField() 
     ) 
    ) \ 
    .annotate(
     views_divided=ExpressionWrapper((F('views')/Decimal(50.0), 
             output_field=FloatField())) 
    ) \ 
    .annotate(weight=F('initial_weight') - F('views_divided')) \ 
    .distinct() 

相貌丑陋,但应该工作(我认为)。

在边注 - 如果你只需要计算weight,你实际上并不需要使用prefetch_relatedselect_realted,Django会照顾这些东西本身(不过,这只是我的猜测 - 如果你真的使用这些外键在代码后面,那么它是合理的)。

+0

非常感谢。 是的,它有点丑,但ExpressionWrapper做的工作! 只有一个音符(有点奇怪)neede dto包裹initial_weight和ExpressionWrapper!我将在最终解决方案中进行编辑。 PS我需要select_related用于其他目的,但它不需要的事实也很有价值!太感谢了。 –

+0

很高兴帮助。如果有人遇到你的问题,我也会更新答案。 –