2010-11-01 83 views
0

不知道如何在Django中完成此操作。在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做....没有办法做到这一点?

回答

2

其实你应该改变你的模型了一下,因为有从LadderPlayer对自身的许多一对多的关系,使用Match作为中间表。关于此主题,请查看django's documentation。那么你应该可以使用django的orm进行你想要的查询!也看看symmetrical/asymmetrical many-to-many relationships

+0

无论如何,他应该能够使用Django ORM进行查询。但它仍然是一个很好的建议:) – desfido 2010-11-02 15:44:24

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}} 
... 

似乎工作我需要....

+0

虽然使用raw始终是一种选择,因为您可以通过Django ORM轻松完成此操作,但最好这样做。 – desfido 2010-11-02 15:42:50

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才能使用它。

如果你想正是你与你的榜样查询呈现同样的信息,我相信这将会是最容易分裂成查询单独药粥获得挑战者& challengee列表 - 例如,像:

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()

+1

那么在这个例子中,如果你想通过只打一个数据库来进行原始查询,那么唯一的原因就是这个例子。只有一个查询,django不会以多对多关系或反向外键关系返回对象! – 2010-11-02 17:20:44

+0

的确如此,但我认为这可能是一个不成熟的优化 - 而且由于提问者在问如何通过ORM来做这件事,我宁可怀疑使用raw()的选择是因为缺乏经验ORM,而不是由于性能方面的考虑。 – desfido 2010-11-02 18:18:14

+0

感谢您的详细回复,我将不得不研究它。初看之下,我认为在LadderPlayer模型中有一个挑战领域似乎不太合适。 “挑战”不是玩家的属性。另外,玩家每次只能面临一次挑战,无论是挑战者还是挑战者。所以挑战必须有两名球员,而一名球员可以参与0或1次挑战......并不是传统的多对多情况。 – chacmool 2010-11-03 21:55:59