对于一个小项目,我有一个匹配和结果的注册表。每场比赛都在球队之间进行(可能是单人球队),并且有一个胜利者。我有Match
和Team
模型,加上MatchTeam
模型。这看起来像(简化)见下面的注释
class Team(models.Model):
...
class Match(models.Model):
teams = ManyToManyField(Team, through='MatchTeam')
...
class MatchTeam(models.Model):
match = models.ForeignKey(Match, related_name='matchteams',)
team = models.ForeignKey(Team)
winner = models.NullBooleanField()
...
现在我想在比赛中做一些统计数据,从查找谁是最能打败你的人开始。我至少不能有效地确定如何做到这一点。
在SQL中(这里只是近似),我的意思是这样的:
SELECT their_matchteam.id, COUNT(*) as cnt
FROM matchteam AS your_mt
JOIN matchteam AS their_mt ON your_mt.match_id = their_mt.match_id
WHERE your.matchteam.id IN <<:your teams>>
your_matchteam.winner = false
GROUP BY their_matchteam.team_id
ORDER BY cnt DESC
(这也需要&#34;他们的_tt不是你的_tt&#34; btw条款,但概念很明确,对吗?)
虽然我没有将其作为SQL进行测试,但它只是为了深入了解我正在寻找的内容:我想通过Django聚合找到这个结果。
根据手册,我可以使用聚合注释结果,在本例中为Count
。正如我在SQL中所做的那样MatchTeams
直接加入MatchTeams
可能是一种捷径,因为它应该&#39;介于两者之间Match
?至少,我不知道如何将其翻译成Django
所以也许我需要为我的团队找到某些匹配项,然后使用其他团队的计数对它们进行注释?但是另一支球队&#39;是什么?
快速撰写如下:
nemesis = Match.objects \
.filter(matchteams__in=yourteams) \
.annotate(cnt=Count('<<otherteam>>')).order_by('-cnt')[0]
如果这是正确的轨道,我该如何定义Count
。
如果它不是正确的轨道,那是什么?
原样,这完全是关于团队而不是用户。这只是为了简单起见:)
另一个问题可能是:我是否应该使用Django ORM的东西,或者我最好只添加SQL?这有一个明显的缺点,就是你要编写非常通用的代码(这甚至可能吗?)或修复你的DB后端。如果不需要,我想避免这种情况。
关于模型:我真的想了解我可以改变模型以使其变得更好,但我无法真正看到没有缺点的解决方案。让我试着解释一下:
我想支持任意数量的球队比赛,例如5队比赛。这意味着我有多对多的关系,而不是一个例如1个匹配2个团队的关系。如果是这种情况,您可以进行非规范化并将获胜者/分数放入团队表中。但事实并非如此。
关于一个团队的结果的额外数据(例如他们的最终得分,他们的时间)根据定义是该关系的属性。它不能进入team
表(因为它将是每个匹配,你可以有一个未定义的匹配数量),并且由于相同的原因,它不能进入匹配表,并作必要的修改。
示例:我有A,B,C,D和E队比赛。 A队和B队有10分,其余都有0分。我想节省积分,A队和B队是这场比赛的赢家。
所以评论表明我需要一个更好的&#39;设计,无论如何,如果你有一个我很乐意看到它,但如果你想支持我支持的东西,它会变得很难。
最后一句话:这个数据可以在SQL中轻松检索,所以这个模型对我来说似乎很好:我只是在Django的初学者中过多而无法在Django&#39;是ORM!
答案 0 :(得分:5)
有趣的问题!我想我有答案(让团队最能打败yourteams
):
Team.objects.get( # the expected result is a team
pk=list( # filter out yourteams
filter(lambda x: x not in [ y.id for y in yourteams ],
list(
Match.objects # search matches
.filter(matchteams__in=yourteams) # in which you were involved
.filter(matchteams__winner=False) # that you loose
.annotate(cnt=Count('teams')) # and count them
.order_by('-cnt') # sort appropriately
.values_list('teams__id', flat=True) # finally get only pks
)
)
)[0] # take the first item that should be the super winner
)
我没有明确地测试它,但如果不起作用,我认为它可能是正确的轨道。
答案 1 :(得分:0)
你可以做这样的事情
matches_won_aginst_my_team = MatchTeam.objects.filter(team=my_team, winner=False).select_related(matches)
teams_won_matches_aginst_my_team = matches_won_aginst_my_team.filter(winner=True).values_list('matchteams__team')
但正如所建议的那样,你可以更好地建模
我会在MatchModel中保存两个字段:home_team,away_team。
更简单,更具指示性。