Django 聚合查询包括零计数

Django Aggregate Query Include Zero Count

在我的 Django 应用程序中,我试图统计所有 Student 提交的 Papers,包括已经提交的学生没有论文(表示为 count=0)。

models.py

class Student(models.Model):
   idstudent = models.AutoField(primary_key=True)
   student_name = models.CharField(max_length=250, null=False, blank=False, verbose_name='Student Name')

class Paper(models.Model):
   idpaper = models.AutoField(primary_key=True)
   student = models.ForeignKey(Student, on_delete=models.PROTECT, null=False, blank=False)

查询尝试 1:Returns 仅提交论文的学生

papers = Paper.objects.order_by('submission_date')
result = papers.values('student', student_name=F('student__student_name')).annotate(count=Count('student')).distinct().order_by('-count')
print(result)       

<QuerySet [{'idstudent': 1, 'student_name': '\nMichael Jordan\n', 'count': 4}, {'idstudent': 2, 'student_name': '\nSteve White\n', 'count': 2}, {'idstudent': 3, 'student_name': '\nHillary Clinton\n', 'count': 1}]>

查询尝试 2:Returns 提交了 0 篇论文的学生,但其他学生的计数为 1

result = Student.objects.values('pk', student_name=F('student_name'))
    .annotate(
        count=Count(
            'pk',
            filter=Q(pk__in=Paper.objects.values('student')
            )
        )
    )
).order_by('-count')
print(result)

<QuerySet [{'idstudent': 1, 'student_name': '\nMichael Jordan\n', 'count': 1}, {'idstudent': 2, 'student_name': '\nSteve White\n', 'count': 1}, {'idstudent': 3, 'student_name': '\nHillary Clinton\n', 'count': 1}, , {'idstudent': 4, 'student_name': '\nDoug Funny\n', 'count': 0}, , {'idstudent': 5, 'student_name': '\nSkeeter Valentine\n', 'count': 0}]>

尝试 2 相同,我还使用 Sum(Case( 尝试了以下结果,结果相同,因为我认识到 尝试 2 raw SQL 实际上利用了 Case(When,但当 Student.pk 出现在 Paper.objects.values 中时,似乎 计数] "list"(虽然不考虑它出现了多少次)。

result = Student.objects.values('pk', student_name=F('student_name')).annotate(
    count=Sum(
        Case(
            When(pk__in=Paper.objects.values('student'), then=1),
            default=0, output_field=IntegerField()
        )
    )
)

<QuerySet [{'idstudent': 1, 'student_name': '\nMichael Jordan\n', 'count': 1}, {'idstudent': 2, 'student_name': '\nSteve White\n', 'count': 1}, {'idstudent': 3, 'student_name': '\nHillary Clinton\n', 'count': 1}, , {'idstudent': 4, 'student_name': '\nDoug Funny\n', 'count': 0}, , {'idstudent': 5, 'student_name': '\nSkeeter Valentine\n', 'count': 0}]>

我如何调整我的查询以包括已提交 0 篇论文的学生,同时保持已提交论文的学生的正确计数?

Along the same lines as Attempt 2, I also tried the following using Sum(Case( which yielded the same result, as I recognized that the Attempt 2 raw SQL actually utilizes Case(When, but seems to only count when Student.pk is present in the Paper.objects.values "list" (while not accounting for how many times it is present).

要么我不理解 problem/question,但您的尝试 2 示例将计数过滤为仅 Paper.objects.values "list",这样的行为正常吗?

你试过简单的吗:

Student.objects.annotate(num_papers=Count('paper'))

如果你想对计数进行额外的过滤,我的建议是使用subqueries这里有一个例子:

Student.objects.annotate(
    num_papers=Subquery(
        Paper.objects.filter(student=OuterRef('pk'))
            # The first .values call defines our GROUP BY clause
            # Its important to have a filtration on every field defined here
            # Otherwise you will have more than one row per group!
            # In this example we group only by student
            # and we already filtered by student.
            # any extra filtration you want should be make here too (before the grouping).
            .values('student')
            # Here we say: count how many rows we have per group 
            .annotate(cnt=Count('pk'))
            # Here we say: return only the count
            .values('cnt')
    )
)