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')
)
)
在我的 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')
)
)