Django加权查询(带注释的值)

时间:2016-11-23 08:08:51

标签: python django django-queryset

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

我需要一些帮助,因为我所遇到的解决方案确实有效,但性能不是我希望的那样

我所拥有的是Media对象。它有相关的评论,喜欢和订单。

目前的工作原理是一个完整的hacky混乱是以下查询:

    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。

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

1 个答案:

答案 0 :(得分: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会自己处理这些事情(但是,这只是我的推测 - 如果你以后在代码中实际使用这些外键,那么它是合理的)。