8

Does select_related work for GenericRelation relations, or is there a reasonable alternative? At the moment Django's doing individual sql calls for each item in my queryset, and I'd like to avoid that using something like select_related.

class Claim(models.Model):
    proof = generic.GenericRelation(Proof)


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

I'm selecting a bunch of Claims, and I'd like the related Proofs to be pulled in instead of queried individually.

4

3 回答 3

18

没有内置的方法可以做到这一点。但是我已经在我的博客select_related上发布了一种模拟泛型关系的技术。


博客内容总结:

我们可以使用 Django 的_content_object_cache字段来创建我们自己select_related的通用关系。

generics = {}
for item in queryset:
    generics.setdefault(item.content_type_id, set()).add(item.object_id)

content_types = ContentType.objects.in_bulk(generics.keys())

relations = {}
for ct, fk_list in generics.items():
    ct_model = content_types[ct].model_class()
    relations[ct] = ct_model.objects.in_bulk(list(fk_list))

for item in queryset:
    setattr(item, '_content_object_cache', 
            relations[item.content_type_id][item.object_id])

在这里,我们获取查询集中关系使用的所有不同内容类型,以及每个对象的不同对象 ID 集合,然后使用内置的 in_bulk 管理器方法一次获取所有内容类型,以备不时之需- 使用以 ID 为键的字典。然后,我们对每种内容类型进行一次查询,再次使用 in_bulk,以获取所有实际对象。

最后,我们简单地将相关对象设置为源项目的 _content_object_cache 字段。我们这样做的原因是,如果您直接调用 x.content_object,Django 会检查并在必要时填充该属性。通过预先填充它,我们确保 Django 永远不需要调用单独的查找 - 实际上我们正在做的是为通用关系实现一种 select_related()。

于 2010-05-31T11:13:33.437 回答
3

您可以使用 .extra() 函数手动提取字段:

Claims.filter(proof__filteryouwant=valueyouwant).extra(select={'field_to_pull':'proof_proof.field_to_pull'})

.filter() 将进行连接, .extra() 将拉出一个字段。proof_proof 是 Proof 模型的 SQL 表名。如果您需要多个字段,请在字典中指定每个字段。

于 2011-02-28T14:03:03.337 回答
3

看起来select_related 和 GRs 不能一起工作。我想您可以为 Claim 编写某种访问器,通过相同的查询获取它们。这篇文章为您提供了一些关于原始 SQL 的指针,以获取通用对象(如果您需要它们)

于 2010-05-30T17:34:42.363 回答