How to sort by annotated Count() in a related model in Django

后端 未结 2 1260
南方客
南方客 2020-12-08 11:03

I\'m building a food logging database in Django and I\'ve got a query related problem.

I\'ve set up my models to include (among other things) a Food model connected

相关标签:
2条回答
  • 2020-12-08 11:58

    Perhaps something like this?

    Food.objects.filter(consumer__user=user)\
                .annotate(consumption_times=Count('consumer'))\
                .order_by('consumption_times')
    
    0 讨论(0)
  • 2020-12-08 12:01

    I am having a very similar issue. Basically, I know that the SQL query you want is:

    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;
    

    What I wish is that you could mix aggregate functions and F expression, annotate F expressions without an aggregate function, have a richer set of operations/functions for F expressions, and have virtual fields that are basically an automatic F expression annotation. So that you could do:

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

    Also, just being able more easily able to add your own complex aggregate functions would be nice, but in the meantime, here's a hack that adds an aggregate function to do this.

    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')
    
    0 讨论(0)
提交回复
热议问题