Django annotation with nested filter

前端 未结 3 1031
萌比男神i
萌比男神i 2021-01-01 12:57

Is it possible to filter within an annotation?

In my mind something like this (which doesn\'t actually work)

Student.objects.all().annotate(Count(\'at

相关标签:
3条回答
  • 2021-01-01 13:13

    Maybe this will work for you:

    excused = Student.objects.filter(attendance__type='Excused').annotate(abs=Count('attendance'))
    

    You need to filter the Students you're looking for first to just those with excused absences and then annotate the count of them.

    Here's a link to the Django Aggregation Docs where it discusses filtering order.

    0 讨论(0)
  • 2021-01-01 13:23

    As of Django 1.8 you can do this directly in the ORM:

    students = Student.objects.all().annotate(num_excused_absences=models.Sum(
        models.Case(
            models.When(absence__type='Excused', then=1),
        default=0,
        output_field=models.IntegerField()
    )))
    

    Answer adapted from another SO question on the same topic

    I haven't tested the sample above but did accomplish something similar in my own app.

    0 讨论(0)
  • 2021-01-01 13:26

    You are correct - django does not allow you to filter the related objects being counted, without also applying the filter to the primary objects, and therefore excluding those primary objects with a no related objects after filtering.

    But, in a bit of abstraction leakage, you can count groups by using a values query.

    So, I collect the absences in a dictionary, and use that in a loop. Something like this:

    # a query for students
    students = Students.objects.all()
    # a query to count the student attendances, grouped by type.
    attendance_counts = Attendence(student__in=students).values('student', 'type').annotate(abs=Count('pk'))
    # regroup that into a dictionary {student -> { type -> count }}
    from itertools import groupby
    attendance_s_t = dict((s, (dict(t, c) for (s, t, c) in g)) for s, g in groupby(attendance_counts, lambda (s, t, c): s))
    # then use them efficiently:
    for student in students:
        student.absences = attendance_s_t.get(student.pk, {}).get('Excused', 0)
    
    0 讨论(0)
提交回复
热议问题