Django Annotation Count with Subquery & OuterRef

nhn9ugyo  于 2023-05-30  发布在  Go
关注(0)|答案(1)|浏览(121)

我试图创建一个高得分统计表/列表的测验,其中表/列表应该是显示的百分比(或总)正确猜测的人,这是猜测。为了进一步阐述,这些是所使用的模型。
Quiz模型:

class Quiz(models.Model):
    participants = models.ManyToManyField(
        User,
        through="Participant",
        through_fields=("quiz", "correct_user"),
        blank=True,
        related_name="related_quiz",
    )
    fake_users = models.ManyToManyField(User, related_name="quiz_fakes")
    user_quizzed = models.ForeignKey(
        User, related_name="user_taking_quiz", on_delete=models.CASCADE, null=True
    )
    time_started = models.DateTimeField(default=timezone.now)
    time_end = models.DateTimeField(blank=True, null=True)
    final_score = models.IntegerField(blank=True, default=0)

这个模型确实也有一些性质;我认为它们与手头的问题无关。
参与者模型:

class Participant(models.Model):  # QuizAnswer FK -> QUIZ
    guessed_user = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="clicked_in_quiz", null=True
    )
    correct_user = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="solution_in_quiz", null=True
    )
    quiz = models.ForeignKey(
        Quiz, on_delete=models.CASCADE, related_name="participants_in_quiz"
    )

    @property
    def correct(self):
        return self.guessed_user == self.correct_user

为了重复我正在尝试做的事情,我将尝试解释我认为这应该如何工作:
1.对于User.objects.all()中的User,找到所有participant对象,其中user.id等于correct_user(来自participant模型)
1.对于每个participant对象,计算correct_user是否== guessed_user
1.对每个participant对象求和,其中上述比较是UserTrue,由字段sum_of_correct_guesses表示
1.返回一个包含所有用户的查询集,参数为[Usersum_of_correct_guesses]
^现在理想情况下应该是percentage_of_correct_guesses,但这是一个事后的想法,应该很容易通过执行sum_of_correct_guesses/ sum n次那个人是一个猜测来改变。
现在,我甚至为一个人编写了一些伪代码,以大致说明如何使用Python算法

# PYTHON PSEUDO QUERY ---------------------
person = get_object_or_404(User, pk=3)  # Example-person
y = Participant.objects.filter(
    correct_user=person
)  # Find participant-objects where person is used as guess
y_corr = []  # empty list to act as "queryset" in for-loop

for el in y:  # for each participant object
    if el.correct:  # if correct_user == guessed_user
        y_corr.append(el)  # add to queryset
y_percentage_corr = len(y_corr) / len(y)  # do arithmetic division
print("Percentage correct: ", y_percentage_corr)  # debug-display
# ---------------------------------------------

我所尝试的(到目前为止还没有成功)是对Count()Q对象使用ExtensionWrapper:

percentage_correct_guesses = ExpressionWrapper(
Count("pk", filter=Q(clicked_in_quiz=F("id")), distinct=True)
/ Count("solution_in_quiz"),
output_field=fields.DecimalField())

all_users = (
User.objects.all().annotate(score=percentage_correct_guesses).order_by("score"))

任何帮助或指导资源如何做到这一点是非常感谢:))

d5vmydt9

d5vmydt91#

我在寻找相关问题时找到了答案:Django 1.11 Annotating a Subquery Aggregate
我所做的是:

  • 创建一个OuterRef()的过滤器,它指向User,并检查User是否与correct_person相同,以及guessed_personcorrect_person之间的比较,在过滤器接受的所有元素的查询集中输出值correct_user
  • 对过滤后的查询集中correct_user的出现次数进行注解计数。
  • 基于annotated-count注解User,这是真正驱动整个操作的注解。注意OuterRef()Subquery是如何用来告诉过滤器哪个用户应该是correct_user的。

下面是我让它工作的代码片段,它看起来与上面链接的问题中的答案非常相似:

from django.db.models import Count, OuterRef, Subquery, F, Q

crit1 = Q(correct_user=OuterRef('pk'))
crit2 = Q(correct_user=F('guessed_user'))
compare_participants = Participant.objects.filter(crit1 & crit2).order_by().values('correct_user')
count_occurrences = compare_participants.annotate(c=Count('*')).values('c')
most_correctly_guessed_on = (
    User.objects.annotate(correct_clicks=Subquery(count_occurrences))
    .values('first_name', 'correct_clicks')
    .order_by('-correct_clicks')
)
return most_correctly_guessed_on

这非常好用,多亏了Oli

相关问题