简体   繁体   English

Django 带有子查询和 OuterRef 的注释计数

[英]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:为了迭代我正在尝试做的事情,我将尝试解释我认为这应该如何工作:

  1. For a 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模型)
  2. For each participant object, evaluate if correct_user == guessed_user对于每个participant correct_user ,评估是否正确用户 == guessed_user
  3. Sum each 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表示
  4. Return a queryset including all users with parameters [ 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:我所做的是:

  • Create a filter with an 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.使用指向UserOuterRef()创建一个过滤器,并检查User是否与 correct_person 相同,以及correct_personcorrect_person之间的比较,在guessed_person集中为过滤器接受的所有元素输出一个值correct_user
  • Do an annotated count for how many occurrences there are of a correct_user in the filtered queryset.对过滤后的correct_user的次数进行带注释的计数。
  • Annotate User based on the annotated-count, this is the annotation that really drives the whole operation. Annotate 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

This works wonderfully, thanks to Oli .感谢Oli ,这非常有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 带有 OuterRef 的 Django 子查询和注释 - Django subquery and annotations with OuterRef 使用 Exists、Subquery 和 OuterRef 优化 django 查询 - Optimize django query with Exists, Subquery and OuterRef 使用 ForeignKey、Django Subquery 和 OuterRef 将一个模型中的字段合并到另一个模型中 - Merging a field in one model to another with ForeignKey, Django Subquery and OuterRef 带有 OuterRef 的简单子查询 - Simple Subquery with OuterRef Django:计数相关 model,其中相关注释具有特定值并在注释中存储计数(或简单地说:计数子查询) - Django: Count related model where an annotation on the related has a specific value and store count in an annotation (or simply: count subquery) Django ORM:如何在不使用rawsql的情况下使用Count()(子查询)过滤注释 - Django ORM: How to filter in an annotation with Count() (subquery) without rawsql OuterRef 如何在 Django 中工作? - How OuterRef works in django? Django:子查询注释 - Django: Annotation on Subquery Django 子查询 outerref('id') 您可能需要添加显式类型转换 - Django subquery outerref('id') You might need to add explicit type casts Django ORM:按数组过滤包含子查询中的 OuterRef 产生零结果 - Django ORM: Filtering by array contains OuterRef within a subquery yields zero results
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM