带有查询api的条件组

时间:2011-11-17 20:35:00

标签: django

设置学生入学制度。 对于学生和课程,我们有N:M关系命名为attandance。 还有一个具有抵抗状态的模型(存在,缺席,合理,......)。

level( id, name, ... )
student ( id, name, ..., id_level )
course( id, name, ... )
status ( id, name, ...)  #present, absemt, justified, ...
attandance( id, id_student, id_course, id_status, date, hour )
   unique_together = ((id_student, id_course, id_status, date, hour),)

我正在查找按%排序的水平> 20%缺席的学生列表。类似的东西:

present = status.objects.get( name = 'present')
justified = status.objects.get( name = 'justified')
absent = status.objects.get( name = 'absent')

#here the question. How to do this:
Student.objects.filter( level = level ).annotate( 
         nPresent =count( attandence where status is present or justified ),
         nAbsent =count( attandence where status is absent ),
         pct = nAbsent / (nAbsent + nPresent ),
      ).filter( pct__gte = 20 ).order_by( "-pct" )

如果无法使用查询API进行查询,则任何解决方法(列表,集合,词典,...)都会很好用!

谢谢!

----此时我手写了一个脏的原始sql --------------------------

select 
                   a.id_alumne, 
                   coalesce ( count( p.id_control_assistencia ), 0 ) as p,
                   coalesce ( count( j.id_control_assistencia ), 0 ) as j,
                   coalesce ( count( f.id_control_assistencia ), 0 ) as f,
                   1.0 * coalesce ( count( f.id_control_assistencia ), 0 ) /
                   ( coalesce ( count( p.id_control_assistencia ), 0 ) + coalesce ( count( f.id_control_assistencia ), 0 ) ) as tpc                   
                from 
                   alumne a 

                   inner join
                   grup g
                       on (g.id_grup = a.id_grup )

                   inner join
                   curs c
                       on (c.id_curs = g.id_curs)

                   inner join
                   nivell n
                       on (n.id_nivell = c.id_nivell)

                   inner join 
                   control_assistencia ca 
                       on (ca.id_estat is not null and 
                           ca.id_alumne = a.id_alumne )

                   inner join
                   impartir i
                       on ( i.id_impartir = ca.id_impartir )

                   left outer join 
                   control_assistencia p
                       on ( 
                           p.id_estat in ( select id_estat from estat_control_assistencia where codi_estat in ('P','R' ) ) and
                           p.id_control_assistencia = ca.id_control_assistencia )

                   left outer join 
                   control_assistencia j
                       on ( 
                           j.id_estat = ( select id_estat from estat_control_assistencia where codi_estat = 'J' ) and
                           j.id_control_assistencia = ca.id_control_assistencia )

                   left outer join 
                   control_assistencia f
                       on ( 
                           f.id_estat = ( select id_estat from estat_control_assistencia where codi_estat = 'F' ) and
                           f.id_control_assistencia = ca.id_control_assistencia )

                where 
                    n.id_nivell = {0} and
                    i.dia_impartir >= '{1}' and
                    i.dia_impartir <= '{2}'

                group by 
                   a.id_alumne

                having
                   1.0 * coalesce ( count( f.id_control_assistencia ), 0 ) /
                   ( coalesce ( count( p.id_control_assistencia ), 0 ) + coalesce ( count( f.id_control_assistencia ), 0 ) )
                   > ( 1.0 * {3} / 100)
                order by
                   1.0 * coalesce ( count( f.id_control_assistencia ), 0 ) /
                   ( coalesce ( count( p.id_control_assistencia ), 0 ) + coalesce ( count( f.id_control_assistencia ), 0 ) )
                   desc   
                '''.format( nivell.pk, data_inici, data_fi, tpc   )

1 个答案:

答案 0 :(得分:2)

如果您不关心它是否在事后使用查询api或python,请使用itertools.groupby。

attendances = Attendance.objects.select_related().filter(student__level__exact=level)
students = []
for s, g in groupby(attendances, key=lambda a: a.student.id):
    g = list(g) # g is an iterator
    present = len([a for a in g if a.status == 'present'])
    absent = len([a for a in g if a.status == 'absent'])
    justified = len([a for a in g if a.status == 'justified'])
    total = len(g)
    percent = int(absent / total)
    students.append(dict(name=s.name, present=present, absent=absent, percent=percent))
students = (s for s in sorted(students, key=lambda x: x['percent']) if s['percent'] > 25)

您可以将生成的dicts列表传递给视图,方法与任何其他查询集相同。