【问题标题】:Django weighted query (annotated values)Django 加权查询(带注释的值)
【发布时间】:2017-04-07 03:24:57
【问题描述】:

我正在尝试创建一个查询并根据自定义的权重计算对其进行排序。

我需要一些帮助,因为我找到的解决方案确实有效,但性能不是我想要的

我拥有的是一个媒体对象。它有相关的评论、点赞和订单。

目前有效但完全是一团糟的是以下查询:

    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')))

但是注释中的类似操作是不可能的(尝试了有和没有 Sum() 的一些变体 - Django 总是抱怨注释值的类型不同。

顺便说一下,我们在这个项目中使用的是 django 1.8。

有没有一种很好的单查询方法来获得我想要的排序权重?

【问题讨论】:

    标签: python django django-queryset


    【解决方案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 会自己处理这些东西(但是,这只是我的猜测 -如果您稍后在代码中实际使用了这些外键,那么这是合理的)。

    【讨论】:

    • 非常感谢。是的,这有点难看,但 ExpressionWrapper 完成了这项工作!只有一个注释(有点奇怪)需要将 initial_weight 和 ExpressionWrapper 包装在里面!我将在最终解决方案中进行编辑。 PS我需要将select_related用于其他目的,但不需要它的事实也很有价值!谢谢一百万。
    • 很高兴它有帮助。我也会更新答案,以防有人偶然发现您的问题。
    猜你喜欢
    • 2020-05-14
    • 1970-01-01
    • 2019-08-03
    • 2018-04-16
    • 2013-08-28
    • 1970-01-01
    • 1970-01-01
    • 2015-09-19
    • 1970-01-01
    相关资源
    最近更新 更多