2009-04-11 78 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爲查詢和數據檢索提供了便捷的方式來無縫訪問關係,同時保持實體的完整性。在這種情況下,你會發現你的Contact對象提供contactphonenumbers_set屬性,您可以用它來訪問通過數據:

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

這意味着,在你的情況下,遍歷所有聯繫人的電話號碼(例如)您可以:

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

如果你真的,真的,真的想做注射出於某種原因,你會看到,你可以做,使用3線的代碼示例立即上面:只需更改打印語句納入分配報表。


在單獨的說明中,僅供將來參考,您可以在不使用SQL語句的情況下編寫注入函數。在Django中,直通表本身就是一個模型,因此您可以直接查詢它:

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 2009-04-11 23:09:50