Buscando ManyToMany objetos a partir de vários objetos através de tabelas intermediárias

votos
1

Existe uma maneira fácil de buscar os objetos ManyToMany de uma consulta que retorna mais de um objeto? A maneira que eu estou fazendo isso agora não se sente tão sexy como eu gostaria que fosse. Aqui está como eu estou fazendo isso agora, na minha opinião:

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

Meus Modelos:

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

Meu Gerente Personalizado:

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
Publicado 11/04/2009 em 03:28
fonte usuário
Em outras línguas...                            


1 respostas

votos
2

Existem algumas coisas que você pode fazer para encontrar sensualidade aqui :-)

O Django não tem qualquer maneira OOTB para injetar as propriedades da tabela através em sua instância Contact. Uma tabela M2M com dados extra é um conceito SQL, então o Django não tentaria lutar contra as relações, nem adivinhar o que deve acontecer em caso de colisão namespace, etc .... Na verdade, eu iria tão longe a ponto de dizer que você provavelmente não quer injetar Propriedades modelo arbitrárias em seu Contactar objeto ... se você achar que precisa de fazer isso, então é provavelmente um sinal de que você deve rever a sua definição do modelo .

Em vez disso, o Django fornece maneiras convenientes para acessar a relação de forma integrada, tanto em consultas e para recuperação de dados, ao mesmo tempo preservando a integridade das entidades. Neste caso, você verá que o seu Contactobjeto oferece uma contactphonenumbers_setpropriedade que você pode usar para acessar o meio de dados:

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

Isso significa, no seu caso, a iteração de todos os números de telefone de contacto (por exemplo), você faria:

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

Se você realmente, realmente, realmente quero fazer a injeção, por algum motivo, você verá que você pode fazer isso usando o exemplo de código 3-line imediatamente acima: basta alterar as instruções de impressão em instruções de atribuição.


Em uma nota separada, apenas para referência futura, você poderia ter escrito a sua função de injetar sem instruções SQL. No Django, a tabela por meio é em si um modelo, para que possa consultá-lo diretamente:

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
Respondeu 11/04/2009 em 12:06
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more