Django中的左连接查询,对同一个表有多个连接

时间:2010-11-01 21:26:28

标签: sql django join

不确定如何在Django中完成此操作。

型号:

class LadderPlayer(models.Model):
  player = models.ForeignKey(User, unique=True)
  position = models.IntegerField(unique=True)

class Match(models.Model):
  date = models.DateTimeField() 
  challenger = models.ForeignKey(LadderPlayer)
  challengee = models.ForeignKey(LadderPlayer)

想要查询以一次性获取有关玩家的所有信息,包括他们发布的任何挑战或针对他们的挑战。这个SQL有效:

select lp.position,
lp.player_id,
sc1.challengee_id challenging,
sc2.challenger_id challenged_by
from ladderplayer lp left join challenge sc1 on lp.player_id = sc1.challenger_id
left join challenge sc2 on lp.player_id = sc2.challengee_id

如果玩家3挑战了玩家2,那么返回这样的东西:

position    player_id   challenging  challenged_by
----------  ----------  -----------  -------------
1           1
2           2                        3
3           3           2

不知道如何在Django ORM中做任何事情?

3 个答案:

答案 0 :(得分:2)

实际上你应该稍微改变你的模型,因为使用LadderPlayer作为中间表Match与自身之间存在多对多的关系。查看有关此主题的django's documentation。然后你应该能够使用django的orm进行你想要的查询!另请查看symmetrical/asymmetrical many-to-many relationships

答案 1 :(得分:1)

好吧,我做了更多挖掘,看起来在Django 1.2中,这可以通过查询管理器上的“raw()”方法来实现。所以这是使用我上面的查询的代码:

ladder_players = LadderPlayer.objects.raw("""select lp.id, lp.position,lp.player_id,
sc1.challengee_id challenging,
sc2.challenger_id challenged_by
from ladderplayer lp left join challenge sc1 on lp.player_id = sc1.challenger_id
left join challenge sc2 on lp.player_id = sc2.challengee_id order by position""")

在模板中,您可以参考“计算”的连接字段:

{% for p in ladder_players %}
{{p.challenging}} {{p.challenged_by}}
...

似乎按我的需要工作......

答案 2 :(得分:1)

@lazerscience绝对正确。你应该调整你的模型,因为你建立了一个事实上的多对多关系;这样做将允许您利用管理界面的更多功能&等等。

此外,无论如何,都不需要转到raw(),因为这可以通过正常使用Django ORM来完成。

类似的东西:

class LadderPlayer(models.Model):
    player = models.ForeignKey(User, unique=True)
    position = models.IntegerField(unique=True)
    challenges = models.ManyToManyField("self", symmetrical=False, through='Match')

class Match(models.Model):
    date = models.DateTimeField() 
    challenger = models.ForeignKey(LadderPlayer)
    challengee = models.ForeignKey(LadderPlayer)

应该是您需要在模型中进行更改的全部内容。然后你应该能够进行像

这样的查询
player_of_interest = LadderPlayer.objects.filter(pk=some_id)
matches_of_interest = \
        Match.objects.filter(Q(challenger__pk=some_id)|Q(challengee__pk=some_id))

获取有关该玩家的所有感兴趣的信息。请注意,您需要from django.db.models import Q才能使用它。

如果您希望完全您使用示例查询提供相同的信息,我相信最简单的方法是将查询拆分为单独的查询以获取挑战者&挑战者名单 - 例如:

challengers = LadderPlayer.objects.filter(challenges__challengee__pk=poi_id)
challenged_by = LadderPlayer.objects.filter(challenges__challenger__pk=poi_id)

将获得感兴趣的玩家的两个相关查询集(w /主键为poi_id)。

如果有一些特殊原因你不希望事实上的多对多关系成为法律上的关系,那么你可以将它们改为

challenger = LadderPlayer.objects.filter(match__challengee__pk=poi_id)
challenged_by = LadderPlayer.objects.filter(match__challenger_pk=poi_id)

因此,对模型更改的建议仅仅是为了帮助利用现有工具,并明确表达您当前隐含发生的关系。

根据您的使用方式,您可能需要执行类似

的操作
pl_tuple = ()
for p in LadderPlayer.objects.all():
    challengers = LadderPlayer.objects.filter(challenges__challengee__pk=p.id)
    challenged_by = LadderPlayer.objects.filter(challenges__challenger__pk=p.id)
    pl_tuple += (p.id, p.position, challengers, challenged_by)
context_dict['ladder_players'] = pl_tuple

在您的视图中为您的模板准备数据。

无论如何,你应该通过Django ORM进行查询,而不是在这种情况下使用raw()