1

有没有一种简单的方法可以从返回多个对象的查询中获取 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
4

1 回答 1

3

你可以做几件事来在这里找到性感:-)

Django 没有任何 OOTB 方式将直通表的属性注入到您的 Contact 实例中。带有额外数据的 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中,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
于 2009-04-11T11:06:21.847 回答