[英]Django Annotation Count with Subquery & OuterRef
I'm trying to create a high score statistic table/list for a quiz, where the table/list is supposed to be showing the percentage of (or total) correct guesses on a person which was to be guessed on.我正在尝试为测验创建一个高分统计表/列表,该表/列表应该显示对要被猜测的人的正确猜测的百分比(或总数)。 To elaborate further, these are the models which are used.
为了进一步阐述,这些是使用的模型。
The Quiz model:测验 model:
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)
This model does also have some properties;这个 model 也有一些属性; I deem them to be unrelated to the problem at hand.
我认为它们与手头的问题无关。
The Participant model:参与者 model:
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
To iterate through what I am trying to do, I'll try to explain how I'm thinking this should work:为了迭代我正在尝试做的事情,我将尝试解释我认为这应该如何工作:
User
in User.objects.all()
, find all participant
objects where the user.id
equals correct_user
(from participant
model)User.objects.all()
中的User
,查找user.id
等于correct_user
的所有participant
对象(来自participant
模型)participant
object, evaluate if correct_user
== guessed_user
participant
correct_user
,评估是否正确用户 == guessed_user
participant
object where the above comparison is True
for the User
, represented by a field sum_of_correct_guesses
participant
object 求和,其中User
的上述比较为True
,由字段sum_of_correct_guesses
表示User
, sum_of_correct_guesses
]User
, sum_of_correct_guesses
] ^Now ideally this should be percentage_of_correct_guesses
, but that is an afterthought which should be easy enough to change by doing sum_of_correct_guesses
/ sum n times of that person being a guess. ^现在理想情况下这应该是
percentage_of_correct_guesses
,但这是事后的想法,应该很容易通过对该人进行sum_of_correct_guesses
/ sum n 次作为猜测来改变。
Now I've even made some pseudocode for a single person to illustrate to myself roughly how it should work using python arithmetics现在我什至为一个人制作了一些伪代码,向自己大致说明它应该如何使用 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
# ---------------------------------------------
What I've tried (with no success so far), is to use an ExtensionWrapper with Count()
and Q
object:我尝试过的(到目前为止没有成功)是使用带有
Count()
和Q
object 的 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"))
Any help or directions to resources on how to do this is greatly appreciated:))非常感谢有关如何执行此操作的任何帮助或资源指示:))
I found an answer while looking around for related problems: Django 1.11 Annotating a Subquery Aggregate我在四处寻找相关问题时找到了答案: Django 1.11 Annotating a Subquery Aggregate
What I've done is:我所做的是:
OuterRef()
which points to a User
and checks if User
is the same as correct_person
and also a comparison between guessed_person
and correct_person
, outputs a value correct_user
in a queryset for all elements which the filter accepts.User
的OuterRef()
创建一个过滤器,并检查User
是否与 correct_person 相同,以及correct_person
和correct_person
之间的比较,在guessed_person
集中为过滤器接受的所有元素输出一个值correct_user
。correct_user
in the filtered queryset.correct_user
的次数进行带注释的计数。User
based on the annotated-count, this is the annotation that really drives the whole operation. User
基于 annotated-count,这是真正驱动整个操作的 annotation。 Notice how OuterRef()
and Subquery
are used to tell the filter which user is supposed to be correct_user
.OuterRef()
和Subquery
来告诉过滤器哪个用户应该是correct_user
的用户。 Below is the code snippet which I made it work with, it looks very similar to the answer-post in the above linked question:下面是我让它工作的代码片段,它看起来与上面链接问题中的答案非常相似:
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_correct_clicks = (
User.objects.annotate(correct_clicks=Subquery(count_occurrences))
.values("first_name", "correct_clicks")
.order_by("-correct_clicks")
)
return most_correct_clicks
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.