Django计数和按两个字段的值分组

时间:2019-01-06 21:08:42

标签: django group-by count annotations aggregate

我有一个这样的模型:

CLASS_TYPE = ( ("A","A Class"), ("B", "B Class") , ("C", "C Class") , ("D", "D Class") ) 

class Student(models.Model):
   systemId    = models.CharField(max_length=15, unique=True )
   studentNo   = models.CharField(max_length=15, unique=True )
   country     = models.CharField(max_length=50)
   clasType    = models.CharField(max_length=2, choices=CLASS_TYPE )
   recordDate = models.DateField() 
...

并且有很多记录。

我很感兴趣,只有两个字段“ 国家”和“ classType ”。
我想根据国家和总和来计算所有班级类型。

我想从list模型中获得这本Student词典:

[
    { "country":"USA" , "A" : Count_Of_A_Class_in_USA , "B" : Count_Of_B_Class_in_USA , "C" : countCinUSA , "D": count_D_in_USA , "Total" : total_students_in_USA } ,
    {"Country":"England" , "A" : A_in_England ...., "C":C_in_England... , "Total" : students_in_England }
]

我怎么得到这个?

1 个答案:

答案 0 :(得分:2)

我们首先可以进行查询,以每个国家/地区和每个clasType来获取Student的数量:

qs = Student.objects.values('country', 'clasType').annotate(
    n=Count('pk')
).order_by('country', 'clasType')

现在,我们可以使用itertools.groupby [Python-doc]将这些字典归为更紧凑的字典:

from itertools import groupby
from operator import itemgetter

data = [
    {'country': c, ** { li['clasType']: li['n'] for li in l } }
    for c, l in groupby(qs, itemgetter('country'))
]

但是我们仍然需要进行“修补”:一个国家可能没有全部clasType,我们可能想为此添加0,此外,我们需要计算总。我们可以这样:

for subd in data:
    total = 0
    for ct, _ in CLASS_TYPE:
        total += subd.setdefault(ct, 0)
    subd['Total'] = total

现在data包含词典列表,每个词典都有一个'country''Total'键,每个CLASS_TYPE有一个键和Student的数量就是那个CLASS_TYPE