2009-04-11 30 views
1

複数のオブジェクトを返すクエリからManyToManyオブジェクトを簡単にフェッチする方法はありますか?私が今やっているやり方は、私が好きなほどセクシーな気がしません。ここで私は私の見解で、今それをやっている方法です:ManyToManyオブジェクトを複数のオブジェクトから中間テーブル経由で取得する

contacts = Contact.objects.all() 
# Use Custom Manager Method to Fetch Each Contacts Phone Numbers 
contacts = PhoneNumber.objects.inject(contacts) 

マイモデル:

class PhoneNumber(models.Model): 
    number = models.CharField() 
    type = models.CharField() 

    # My Custom Manager 
    objects = PhoneNumberManager() 

class Contact(models.Model): 
    name = models.CharField() 
    numbers = models.ManyToManyField(PhoneNumber, through='ContactPhoneNumbers') 

class ContactPhoneNumbers(models.Model): 
    number = models.ForeignKey(PhoneNumber) 
    contact = models.ForeignKey(Contact) 
    ext = models.CharField() 

マイカスタムマネージャー:

class PhoneNumberManager(models.Manager): 
    def inject(self, contacts): 
     contact_ids = ','.join([str(item.id) for item in contacts]) 
     cursor = connection.cursor() 

     cursor.execute(""" 
      SELECT l.contact_id, l.ext, p.number, p.type 
      FROM svcontact_contactphonenumbers l, svcontact_phonenumber p 
      WHERE p.id = l.number_id AND l.contact_id IN(%s) 
      """ % contact_ids) 

     result = {} 
     for row in cursor.fetchall(): 
      id = str(row[0]) 
      if not id in result: 
       result[id] = [] 

      result[id].append({ 
       'ext': row[1], 
       'number': row[2], 
       'type': row[3] 
      }) 

     for contact in contacts: 
      id = str(contact.id) 
      if id in result: 
       contact.phonenumbers = result[id] 

     return contacts 

答えて

2

あなたが見つけるために行うことができますカップルの事があります。ここでセクシーさ:-)

Djangoはあなたの連絡先にスルーテーブルのプロパティを注入するためのOOTBの方法を持っていませんインスタンス。余分なデータを持つM2MテーブルはSQLの概念なので、Djangoは名前空間の衝突などで何が起こるべきかを推測することはできません。実際には、Contactオブジェクトに任意のモデルプロパティを注入したくないと言っていることは間違いありません。あなた自身がそれを行う必要がある場合は、おそらくモデル定義を修正する必要があるということです。

代わりに、Djangoは問合せとデータ検索の両方でシームレスにリレーションにアクセスする便利な方法を提供し、その間、すべてのエンティティの整合性を維持します。

>>> c = Contact.objects.get(id=1) 
>>> c.contactphonenumbers_set.all() 
# Would produce a list of ContactPhoneNumbers objects for that contact 

この手段は、あなたのケースでは、すべての連絡先の電話番号の反復する(例:この場合、あなたはあなたのContactオブジェクトを使用して、データを介してアクセスするために使用することができますcontactphonenumbers_setプロパティを提供することを見つけることができます)あなたが希望:

for contact in Contact.objects.all(): 
    for phone in contact.contactphonenumbers_set.all(): 
     print phone.number.number, phone.number.type, phone.ext 

あなたは本当に、本当に、本当に何らかの理由で注入を行いたい場合は、あなたはすぐ上3行のコードサンプルを使用することを行うことができます表示されます:ちょうどprint文を変更代入文に変換する。


別途メモとして、後で参考にして、SQL文なしでinject関数を記述できました。 Djangoでは、throughテーブル自体がモデルなので、直接問い合わせることができます:

def inject(self, contacts): 
    contact_phone_numbers = ContactPhoneNumbers.objects.\ 
          filter(contact__in=contacts) 
    # And then do the result construction... 
    # - use contact_phone_number.number.phone to get the phone and ext 
    # - use contact_phone_number.contact to get the contact instance 
+0

うん、これは間違っている可能性があります。 cons = Contact.objects.all()[0:50]そしてnum = ContactPhoneNumbers.objects.filter(contact__in = cons)は、150以上のデータベースクエリを実行します。 – Matt

+0

あああ! contact_phone_numbers = ContactPhoneNumbers。 objects.select_related().filter(contact__in = contacts) 非常にセクシーです。 – Matt

+0

私はそれが連絡先をもう一度引っ張らないほどスマートであるかどうか疑問に思います。ちょっと質問を調べなければなりません。 – Matt

関連する問題