【问题标题】:Django - How to get the top 10 highest scores, but limiting it to a single score per user?Django - 如何获得前 10 名的最高分,但将其限制为每个用户的一个分数?
【发布时间】:2018-03-21 01:32:07
【问题描述】:

我有一个简单的模型来保存任意数量的基于分数的游戏的分数:

class Score(models.Model):
    game = models.ForeignKey(Game, related_name='leaderboards')
    value = models.IntegerField(db_index=True)
    uom = models.CharField('Unit of Measurement', max_length=10)

    user = models.ForeignKey(settings.AUTH_USER_MODEL)

    class Meta:
        ordering = ['-value']

    ...

我的 Django Rest Framework API 视图如下,旨在让我获得特定游戏的当前排行榜:

class LeaderboardView(APIView):
    ...

    def get(self, request, pk):
        game = get_object_or_404(Game, pk=pk)

        # Get all scores order by highest to lowest
        scores = (Score.objects.select_related('user').
                  filter(game=game).
                  order_by('-value'))

        # Create and ordered dict to preserve order added
        # and only add user scores who have not been added yet
        top10 = collections.OrderedDict()
        for obj in scores:
            if obj.user.pk not in top10 and len(top10) < 10:
                top10[obj.user.pk] = obj

        # Turn ordered dict back into a QuerySet list
        results = []
        for obj in top10.itervalues():
            results.append(obj)

    serializer = ScoreSerializer(results, many=True)
    return Response(serializer.data)

问题是我无法弄清楚如何获得前 10 名的最高分,但将其限制为每个用户的单个分数(例如 - 他们的最高分)使用 ORM 而不是手动获取所有分数,然后遍历它们以确保我只获得每个用户的最高分数。

虽然我可怜的尝试得到我需要的结果,但它一定是非常低效的,并且必须有更好的方法来使用 ORM 的力量来获得这些结果。

提前感谢您的帮助。

【问题讨论】:

    标签: django


    【解决方案1】:

    Max 使用注释和聚合。有点像:

    from django.db.models.aggregates import Max
    
    leaderboard_data = Score.objects.filter(game=game) \
                                    .values('user') \
                                    .annotate(max_value=Max('value')) \
                                    .order_by('-max_value')
    

    这大致转换为带有Max 运算符的GROUP BY 查询。

    SELECT "user_id", MAX("value") AS "max_value" 
    FROM "score"
    WHERE "game_id" = 1231
    GROUP BY "user_id" 
    ORDER BY "max_value" DESC
    

    【讨论】:

    • 谢谢@Saurabh!虽然这在技术上为我提供了数据,但我无法将生成的查询集传递给序列化程序,因为数据被扁平化为:{"user_id": 1, "max_value": 100} 用于每个结果。虽然我可以将user__usernameuser__avatar 以及我需要的任何其他相关字段添加到值列表中,但它当然仍然是平坦的,然后需要另一个序列化程序才能将数据转换为我需要的输出格式。虽然我的方法有点混乱,但它最终完成了我需要的工作,而无需为此创建另一个序列化程序。
    • 一旦你拥有了你需要的所有用户,你应该能够使用 User.objects.filter(id__in=users_ids) 检索所有用户的详细信息。现在,你真的可以在正常意义上直接使用序列化器了。但是您应该能够非常轻松地构建自定义 json 响应,而不会遇到太多麻烦。
    • 我采用了自定义序列化程序方法,只抓取了单个查询中的字段(例如-user__username 等)。但是,您是对的,我本可以进行第二次查询以获取所有用户。再次感谢。
    猜你喜欢
    • 2021-02-12
    • 2022-01-20
    • 2015-10-22
    • 2022-01-25
    • 2020-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多