【问题标题】:Django Aggregate Query Include Zero CountDjango 聚合查询包括零计数
【发布时间】:2023-03-19 18:30:01
【问题描述】:

在我的 Django 应用程序中,我试图获取所有 Student 提交 Paper 的计数,包括提交 NO 论文的学生(表示为 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:仅返回已提交论文的学生

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:返回已提交 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}]>

Attempt 2 相同,我还使用 Sum(Case( 尝试了以下操作,产生了相同的结果,因为我认识到 Attempt 2 原始 SQL 实际上使用Case(When,但似乎Student.pk 出现在Paper.objects.values“列表”中时计数(而不考虑它出现的多少次) .

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 篇论文的学生,同时又保持正确的学生数量?

【问题讨论】:

    标签: django django-views django-queryset django-q


    【解决方案1】:

    与尝试 2 相同,我也尝试了以下使用 Sum(Case( 产生了相同的结果,因为我认识到 尝试 2 原始 SQL 实际使用 Case(When, 但似乎只计算 当 Student.pk 出现在 Paper.objects.values “列表”中时(而 不考虑它出现了多少次)。

    要么我不理解问题/问题,但您的尝试 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')
        )
    )
    

    【讨论】:

    • Student.objects.annotate(num_papers=Count('paper_set'))
    • 不,它的paperpaper_set 是您进行预取的时候。
    • 看看docs,如果你先做过滤,那么计数也会被过滤。但是如果你过滤,你不会得到没有论文的学生吗?我认为获得所需内容的最佳方法是使用子查询来进行计数,这样您就可以最准确地了解要依靠的内容。我将在我的答案中发布更新,以便让您了解如何使用子查询进行操作。
    • 我认为我在最初的问题中未能传达的是,如果我查询一部分论文,这仍然有效吗?例如,...Count('paper')).filter('paper__in=subset')。我想这就是我遇到麻烦的地方。在那种情况下,我似乎不再得到 0 计数。我最终要做的是设置带有显示动态计数的标签的表单过滤器,并在应用过滤器时调整这些计数。
    • 我已经用一个子查询示例更新了答案,如果你仍然无法做到这一点,那么我可能需要更多代码(可能来自表单)才能得到你。
    猜你喜欢
    • 2011-09-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-01
    • 1970-01-01
    • 2021-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多