在GenericForeignKeys上反向使用prefetch_related

时间:2014-10-13 22:27:00

标签: sql django

我有动物和笼子。

from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.db import models

class Cage(models.Model):
    ...

class Animal(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')
    ...

首先是模型Cage。我正在尝试优化迭代Cage列表时所做的查询次数。

然后我们有模型Animal。动物包含GenericForeignKey。 Animal指出的众多物品中的一个可以是一个笼子,但也可以是其他物品,比如说食物。

如果我想获得Cage 16中的所有Animal对象,那将很容易。声明如下:

Animal.objects.filter(content_type=ContentType.objects.get_for_model(Cage), object_id=16)
因此,我可以通过以下方式获取每只笼子中的动物:

cage_content_type_id = ContentType.objects.get_for_model(Cage).id
animals_in_cage = {}
for cage in cages:
    animals_in_cage[cage.id] = Animal.objects.filter(
        content_type_id=cage_content_type_id,
        object_id=cage.id
    )

此代码的问题在于它将O(n) SQL查询n = len(cages)。理想情况下,我可以使用prefetch_related之类的东西提前在动物和笼子表上进行连接。但是,我无法通过GenericForeignKeys向相反方向移动来找出任何方法。基本上我想做的是:

cages = Cage.objects.prefetch_related('animal').all()

但是动物不是Cage对象的属性,在这种情况下,cage不是Animal对象的属性。两者之间的链接是Animal模型中的内容类型和对象ID,对于某些实例,它代表一个笼子。

对于如何实现这一点的任何想法将不胜感激!

1 个答案:

答案 0 :(得分:1)

如果凯奇有这样的事情:

animals = GenericRelation(Animal)

你可以去:

Cage.objects.filter(...).prefetch_related('animals').all()

(至少在Django 1.8.4中。)