如何在Django ORM中进行多次连接和聚合?

2 投票
1 回答
1767 浏览
提问于 2025-04-16 11:17

我是Django/Python/ORM的新手!我对ORM中的连接(joins)有点搞不清楚。

这是我的模型:

class Courts(models.Model):
    id = models.AutoField(primary_key=True)
    location_name = models.CharField(max_length=100)
    number = models.IntegerField()
    def __unicode__(self):
        return "%s %s %s" % (self.id, self.location_name, self.number)

class Matches(models.Model):
    id = models.AutoField(primary_key=True)
    date = models.DateTimeField()
    court = models.ForeignKey(Courts)
    def __unicode__(self):
        return "%s %s" % (self.id, self.date)

class Participants(models.Model):
    id = models.AutoField(primary_key=True)
    match = models.ForeignKey(Matches)
    userid = models.ForeignKey(User)
    games_won = models.IntegerField()
    def __unicode__(self):
        return "%s %s %s" % (self.id, self.games_won, self.userid)

第一步是把所有的“参与记录”整理成下面这样的输出:

[match_id] [date]       [userid]  [games_won]  [court_location_name]  [court_number]
1          01-01-2011   mike      6            Queen                  5
1          01-01-2011   chris     4            Queen                  5
2          01-02-2011   bob       3            Queen                  6
2          01-02-2011   joe       4            Queen                  6
3          01-03-2011   jessie    5            Queen                  2
3          01-03-2011   john      5            Queen                  2

我应该写什么样的ORM脚本来实现这个?我对ORM中简单的连接是怎么工作的都很困惑,更别提要把三个表结合在一起了。

接下来,我想把数据汇总成最终看起来像这样的格式:

[match_id] [date]       [player1] [player2]  [p1wins] [p2wins] [game_winner] [court_location_name]  [court_number]
1          01-01-2011   mike      chris      6        4        mike          Queen                  5
2          01-02-2011   bob       joe        3        4        joe           Queen                  6
3          01-03-2011   jessie    john       5        5        draw          Queen                  2  

这会改变我在视图中写的ORM脚本吗?这是我需要放在视图里还是模板里呢?

更新:

我想我可以使用 select_related()。所以我试了 Participants.objects.select_related(),然后得到了这个SQL语句:

SELECT "squash_participants"."id", "squash_participants"."match_id", "squash_participants"."userid_id", "squash_participants"."games_won", "squash_matches"."id", "squash_matches"."date", "squash_matches"."court_id", "squash_courts"."id", "squash_courts"."location_name", "squash_courts"."number", "auth_user"."id", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."password", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."is_superuser", "auth_user"."last_login", "auth_user"."date_joined" FROM "squash_participants" INNER JOIN "squash_matches" ON ("squash_participants"."match_id" = "squash_matches"."id") INNER JOIN "squash_courts" ON ("squash_matches"."court_id" = "squash_courts"."id") INNER JOIN "auth_user" ON ("squash_participants"."userid_id" = "auth_user"."id"

这看起来更像我想要的结果。现在我不知道怎么把这些数据提取到模板里。

更新2:

我的视图是这样的:

def index(request):
    matches_list = Participants.objects.all()
    return render_to_response('squash/base_matches.html', {'matches_list': matches_list}, context_instance = 
RequestContext(request))
    return HttpResponse(output)

而我的模板是这样的:

{% for matches in matches_list %}
    <tr>
        <td>{{ matches.id }}</td> 
        <td>{{ matches.date|date:"d-m-Y" }}</td>
        <td>{{ matches.date|date:"G:i" }}</td>
    </tr>
    {% endfor %}

它能正确显示所有的 Participants.id,但比如说比赛的ID、日期或场地却没有显示出来。

1 个回答

5

你提到的有几个“事情”:

  1. 定义“id”这个主字段其实没必要,反而会让人困惑,因为Django会自动生成它。
  2. 给你的模型起名字时,最好用单数形式(比如用Match而不是Matches等)。这样做可以让Django更好地处理单数和复数的关系。这也很合理,因为Match这个类的对象代表的是一场比赛,而不是多场比赛;如果要处理多场比赛,你可以用模型管理器:Match.objects。
  3. 你的“Participants”模型实际上是一个中间的多对多关系表,你可以在“Matches”模型中明确声明这一点,具体可以参考这里

关于你的JOIN问题,我支持S. Lott的观点,我在StackOverflow上看到过 - 在Django中不应该考虑JOIN,因为Django的ORM(“O”代表“对象” - 把SQL映射到对象上并不是一件简单的事)并不是这样工作的。你应该专注于完成你的工作,只有在确实需要的情况下,比如性能问题,才考虑使用JOIN。

最后,关于你的问题的答案:

Participants.objects.all()

:-)

你可以通过使用select_related()来提高性能,如果需要的话,还可以使用order_by()来进行排序。

至于聚合,其他人可能会给出一个简单的解决方案,但我现在看不到。不过,你可以在“Matches”模型中添加这个:

players = models.ManyToManyField(User, through='Participants')

然后你可以用最简单的方式获取比赛:

Matches.objects.all()

这样返回的每个元素都会有“players”属性。

更新:

要把数据传递给模板,你只需要把它添加到模板渲染的上下文中:

http://docs.djangoproject.com/en/1.2/ref/templates/api/#rendering-a-context

这可以通过使用render_to_response这个快捷函数来简化。

撰写回答