基于多种关系优化模型设计

时间:2019-05-24 22:18:58

标签: python django django-models

我正在尝试创建一个排行榜,我试图确定一个最佳的结构来避免出现问题。

目前,我有三个模型代表我要收集的数据。 SeasonChallengeAttempt

class Season(models.Model):
    season = models.CharField(max_length=4)
    start_date = models.DateTimeField()
    end_date = models.DateTimeField()


class Challenge(models.Model):
    season = models.ForeignKey(Season, on_delete=models.CASCADE)
    name = models.CharField(max_length=255)
    start_date = models.DateTimeField()
    end_date = models.DateTimeField()

class Attempt(models.Model):
    challenger = models.ForeignKey(User, on_delete=models.CASCADE)
    challenge = models.ForeignKey(Challenge, on_delete=models.CASCADE)
    points = models.IntegerField(default=0)

现在,我想从我希望得到的内容中得出以下结论。

  1. 一个季节持续一年
  2. 挑战持续了一个月
  3. 一个季节可能会有很多挑战
  4. 挑战可以尝试多次
  5. 一项挑战不能超过一个赛季
  6. 一次尝试不能属于多个挑战。

我尝试显示数据时会遇到困难。我想返回当前赛季的排行榜,该排行榜计算出每个挑战用户的最佳尝试,并返回一个包含该用户及其整个赛季总积分的查询集。

示例数据

Season(season='2019',
       start_date=...,  # some date object
       end_date=...,  # some date object
)

Challenge(season=1,  # represents season object above
          name='Challenge 1',
          start_date=...,  # some date object
          end_date=...,  # some date object
)

Challenge(season=1,  # represents season object above
          name='Challenge 2',
          start_date=...,  # some date object
          end_date=...  # some date object
)

Attempt(challenger=1,  # represents user object
        challenge=1,  # represents challenge 1 above
        points=5
)

Attempt(challenger=2,  # represents user object
        challenge=1,  # represents challenge 1 above
        points=10
)

Attempt(challenger=1,  # represents user object
        challenge=2,  # represents challenge 2 above
        points=5
)


Attempt(challenger=2,  # represents user object
        challenge=2,  # represents challenge 2 above
        points=10
)

预期结果

  1. 挑战者2-20分
  2. 挑战者1-10分

如何使该模型更好地表示我需要的结果。我已经看过直通关系,但不确定是否完全了解或在这里是否需要。

然后我将如何使用ORM获得所需的结果

1 个答案:

答案 0 :(得分:1)

您的模型很好,请不要更改。为获得所需的结果,请尝试如下操作:

Attempt.objects.filter(challenge__season__season='2019').values('challenger__name').annotate(total_points=Sum('points')).order_by('-total_points')

这将进行所有尝试,将其过滤为名称为'2019'的季节,然后获取所有挑战者名称,然后将其汇总为挑战者尝试点的总和(这似乎是您想要的,但是您也可以使用“ Max”代替“ Sum”。

这种方法的一个问题(顺便说一句,我必须归功于此消息来源:https://simpleisbetterthancomplex.com/tutorial/2016/12/06/how-to-create-group-by-queries.html)是,如果多个挑战者具有相同的名称,他们将被分组在一起,这可能是您不想要的。因此,您可能希望改为按挑战者“ pk”和“名称”分组。