2010-11-01 126 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