2011-01-18 4 views
15

私は、外部キー関係を自己参照して、このモデルを持っている:Djangoの自己再帰のForeignKeyのフィルタクエリ

class Person(TimeStampedModel): 
    name = models.CharField(max_length=32) 
    parent = models.ForeignKey('self', null=True, blank=True, related_name='children') 

は、今私は人のために、すべてのマルチレベルの子を取得したいです。どのようにしてDjangoのクエリを書くのですか?再帰関数のように動作する必要があります。

答えて

24

...)

でも、errxの推奨通りにmpttを推奨しています。

+0

get_all_children()を関数呼び出しとして、r = r + c.get_all_childrenを使う必要があります。 ()またはあなたはネストされたリストで終わるでしょう。私は自分自身を編集する権利を持っていないようです – alan

+0

コメントに応じてコードを更新しました。誰もがすべての投稿を編集することが許可されている場合、私たちは大きな問題を抱えているでしょう:) – sunn0

+1

これは内部関数呼び出しにも 'include_self = True'があるはずです。さもなければ、それぞれの再帰で、もう一度レベルを下げて、実際には何もrに加えないでください。その変更を加えた後、私は正しく機能しました。 – andy

12

Modified preorder tree traversalについてお読みください。 ここにdjangoの実装があります。

EDIT:SeomGi漢

def get_all_children(self, include_self=True): 
    r = [] 
    if include_self: 
     r.append(self) 
    for c in Person.objects.filter(parent=self): 
     _r = c.get_all_children(include_self=True) 
     if 0 < len(_r): 
      r.extend(_r) 
    return r 

あなたは再帰や大量のデータを持っている場合(これを使用しないでくださいに応じて補正をあなたは常にあなたのモデルに再帰関数を追加することができます https://github.com/django-mptt/django-mptt/

+1

それはサードパーティのソリューションを使用する必要がありますか?他の方法はありますか? – Ahsan

+2

@Ashan:あなたはいつでもそれについて読むことができます。ここでは:http://dev.mysql.com/tech-resources/articles/hierarchical-data.htmlそしてあなた自身でコードを書く –

5

あなたのツリーの最大の深さを知っている場合は、あなたが(未テスト)このような何かを試みることができる:

Person.objects.filter(Q(parent=my_person)|Q(parent__parent=my_person)| Q(parent__parent__parent=my_person)) 
6

sunn0の提案は素晴らしいアイデアですが、get_all_children()は、奇妙な結果を返します。それは[Person1、[Person3、Person4]、[]]のようなものを返します。以下のように変更する必要があります。チームメンバー与えられた前記

def get_all_children(self, include_self=True): 
    r = [] 
    if include_self: 
     r.append(self) 
    for c in Person.objects.filter(parent=self): 
     _r = c.get_all_children(include_self=True) 
     if 0 < len(_r): 
      r.extend(_r) 
    return r 
0

は、私は、非常によく似たbusiness problemを持っていた、私は彼の下の下で完全なチームを見つけることになりました。しかし、多数の従業員を抱えることは、再帰的ソリューションを非常に非効率的にし、APIもサーバーからタイムアウト・エラーを受けていました。

受け入れられたソリューションは、ノードを受け取り、最初の子になり、階層の一番下まで深くなります。次に、2番目の子に(もし存在すれば)再び戻ってきてから、もう一度下に行く。つまり、すべてのノードを1つずつ調べ、すべてのメンバーを配列に追加します。これは多くのdbコールにつながり、探索すべきノードが膨大な数になる場合は避けるべきです。私が思いついた解決策は、ノードをレイヤー単位でフェッチします。 dbコールの数は、レイヤーの数に等しい。解決方法はこちらSO linkをご覧ください。

0

私はこれが古いと知っていますが、誰かが助けになるかもしれません。

Personモデルのインスタンスを考えると、言うperson、あなたは、単に行うことができます。

children = person.children.all() # children being the related_name of the recursive field