2009-04-11 4 views
1

Gibt es eine einfache Möglichkeit, die ManyToMany-Objekte von einer Abfrage abzurufen, die mehr als ein Objekt zurückgibt? Die Art, wie ich es mache, fühlt sich jetzt nicht so sexy an, wie ich es gerne hätte. Hier ist, wie ich es jetzt meiner Meinung nach mache:ManyToMany-Objekte aus mehreren Objekten über Zwischentabellen holen

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

Meine Models:

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

My Custom Manager:

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 

Antwort

2

Es gibt ein paar Dinge, die Sie tun können, finden Sexiness hier :-)

Django hat keine OOTB Weise, die Eigenschaften der durch Tabelle in Ihren Kontakt zu injizieren Beispiel. Eine M2M-Tabelle mit zusätzlichen Daten ist ein SQL-Konzept, also würde Django nicht versuchen, die Beziehungen zu bekämpfen, noch raten, was im Falle einer Namespace-Kollision usw. passieren soll. Ich würde sogar so weit gehen, zu sagen, dass Sie wahrscheinlich keine beliebigen Modelleigenschaften auf Ihr Kontaktobjekt injizieren wollen ... wenn Sie sich dazu gezwungen sehen, dann ist es wahrscheinlich ein Zeichen, dass Sie Ihre Modelldefinition überarbeiten sollten .

Stattdessen bietet Django bequeme Möglichkeiten für den nahtlosen Zugriff auf die Relation, sowohl bei Abfragen als auch beim Datenabruf, wobei die Integrität der Entitäten erhalten bleibt. In diesem Fall werden Sie feststellen, dass Ihr Contact Objekt eine contactphonenumbers_set Eigenschaft bietet, die Sie durch die Daten verwenden können, um Zugriff:

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

Das bedeutet, in Ihrem Fall aller Kontakttelefonnummern (zB iterieren) würden Sie:

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

Wenn Sie wirklich, wirklich, wirklich die Injektion aus irgendeinem Grund tun wollen, werden Sie Sie tun können sehen, dass mit der 3-zeiligen Codebeispiel unmittelbar über: nur die print-Anweisungen ändern in Zuweisungsanweisungen.

In einer separaten Notiz, nur für zukünftige Referenz, könnten Sie Ihre inject-Funktion ohne SQL-Anweisungen geschrieben haben. In Django ist die durchgehende Tabelle selbst ein Modell, so dass Sie es direkt abfragen können:

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

Hmm, ich kann das falsch machen. running: cons = Contact.objects.all() [0:50] und dann num = ContactPhoneNumbers.objects.filter (contact__in = cons) führt über 150 Datenbankabfragen aus. – Matt

+0

Ahh hah! contact_phone_numbers = Kontakttelefonnummern. objects.select_related() .filter (contact__in = contacts) Das ist sehr sexy. – Matt

+0

Ich frage mich, ob es schlau genug ist, die Kontakte nicht wieder einzuziehen. Ich werde die Abfrage in Kürze prüfen müssen. – Matt

Verwandte Themen