为什么django的prefetch_related()只能和all()而不是filter()一起工作?

假设我有这个模型:

class PhotoAlbum(models.Model): title = models.CharField(max_length=128) author = models.CharField(max_length=128) class Photo(models.Model): album = models.ForeignKey('PhotoAlbum') format = models.IntegerField() 

现在,如果我想有效地查看专辑子集中的照片子集。 我这样做:

 someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set") for a in someAlbums: somePhotos = a.photo_set.all() 

这只有两个查询,这是我所期望的(一个拿到相册,然后像`SELECT * IN photos WHERE photoalbum_id IN()。

一切都很好。

但是,如果我这样做:

 someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set") for a in someAlbums: somePhotos = a.photo_set.filter(format=1) 

然后它用WHERE format = 1做了大量的查询! 我做错了什么或是Django不够聪明,意识到它已经提取所有的照片,并可以在Python中过滤它们? 我发誓我在文档中的某个地方阅读,应该这样做…

在Django 1.6和更早的版本中,不可能避免额外的查询。 prefetch_related调用有效地caching查询a.photoset.all()每个专辑的a.photoset.all()的结果。 但是, a.photoset.filter(format=1)是一个不同的查询集,因此您将为每个专辑生成一个额外的查询。

这在prefetch_related文档中解释。 filter(format=1)相当于filter(spicy=True)

请注意,您可以通过在python中过滤照片来减less数量或查询:

 someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set") for a in someAlbums: somePhotos = [p for p in a.photoset.all() if p.format == 1] 

在Django 1.7中,有一个Prefetch()对象允许您控制prefetch_related的行为。

 from django.db.models import Prefetch someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related( Prefetch( "photo_set", queryset=Photo.objects.filter(format=1), to_attr="some_photos" ) ) for a in someAlbums: somePhotos = a.some_photos 

有关如何使用Prefetch对象的更多示例,请参阅prefetch_related文档。

从文档 :

…和QuerySets一样,任何暗示不同数据库查询的后续链接方法都将忽略以前的caching结果,并使用新的数据库查询检索数据。 所以,如果你写下面的话:

披萨= Pizza.objects.prefetch_related('toppings')[list(pizza.toppings.filter(spicy = True))比萨比萨]

…那么事实上pizza.toppings.all()被预取不会帮助你 – 事实上,它损害了性能,因为你已经完成了一个你没有使用过的数据库查询。 所以请谨慎使用此function!

在你的情况下,“a.photo_set.filter(format = 1)”被视为一个新的查询。

另外,“photo_set”是一个反向查找 – 通过一个不同的pipe理器完成。