为什么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理器完成。