如何通过注解COUNT()在Django的相关模型进行排序(How to sort by annot

2019-07-17 10:38发布

我建立在Django食物日志数据库和我有一个查询相关的问题。

我已经建立了我的模型,包括(除其他事项外),通过消费模型通过M2M场“消费者”连接到用户模型中的食品模型。 食品模型描述小菜和消费模型描述食品(日期,金额等)的用户的消费。

class Food(models.Model):
    food_name = models.CharField(max_length=30)
    consumer = models.ManyToManyField("User", through=Consumption)

class Consumption(models.Model):
    food = models.ForeignKey("Food")
    user = models.ForeignKey("User")

我想创建一个返回由食品对象出现在消费表用户(次用户消费的食品数量)的次数下令所有食品对象的查询。

我想在行的内容:

Food.objects.all().annotate(consumption_times = Count(consumer)).order_by('consumption_times')`

但是,这当然算有关食品对象的所有消费对象,不只是与用户相关联的人。 我需要改变我的模型或我只思念的东西查询明显?

这是一个漂亮的时间要求严格的操作(除其他事项外,它被用来填补在前端的自动完成场)和食物表有几千项,所以我宁愿做排序在数据库端,而不是做蛮力方法和遍历结果做:

Consumption.objects.filter(food=food, user=user).count()

然后使用Python排序对它们进行排序。 我不认为这方法将很好地进行缩放随着用户基数增大,我想数据库,我可以从一开始就设计为面向未来。

有任何想法吗?

Answer 1:

也许这样的事情?

Food.objects.filter(consumer__user=user)\
            .annotate(consumption_times=Count('consumer'))\
            .order_by('consumption_times')


Answer 2:

我有一个非常类似的问题。 基本上,我知道你想要的SQL查询:

SELECT food.*, COUNT(IF(consumption.user_id=123,TRUE,NULL)) AS consumption_times
       FROM food LEFT JOIN consumption ON (food.id=consumption.food_id)
       ORDER BY consumption_times;

我希望的是,你可以混合聚合函数和F表达,诠释˚F表情没有聚合函数,具有更丰富的适用于F表达操作/功能,并且具有基本上自动˚F表达式标注虚拟领域。 所以,你可以这样做:

Food.objects.annotate(consumption_times=Count(If(F('consumer')==user,True,None)))\
            .order_by('consumtion_times')

此外,仅仅能够能够更容易地添加自己的复杂的聚合函数将是很好的,但在此期间,这里是增加了集合函数来做到这一点黑客攻击。

from django.db.models import aggregates,sql
class CountIf(sql.aggregates.Count):
    sql_template = '%(function)s(IF(%(field)s=%(equals)s,TRUE,NULL))'
sql.aggregates.CountIf = CountIf

consumption_times = aggregates.Count('consumer',equals=user.id)
consumption_times.name = 'CountIf'
rows = Food.objects.annotate(consumption_times=consumption_times)\
                   .order_by('consumption_times')


文章来源: How to sort by annotated Count() in a related model in Django