orm - 为什么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中过滤它们? 我发誓我在文档中的某个地方读到它本应该这样做......


2个解决方案



142 votes



在Django 1.6及更早版本中,无法避免额外的查询。 ​​prefetch_related​​呼叫有效地缓存了查询集中每个专辑的​​filter(spicy=True)​​的结果。 但是,​​a.photoset.filter(format=1)​​是一个不同的查询集,因此您将为每个专辑生成一个额外的查询。

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

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



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


 

在Django 1.7中,有一个​​prefetch_related​​对象,允许您控制​​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


 

 


每天逼着自己写点东西,终有一天会为自己的变化感动的。这是一个潜移默化的过程,每天坚持编编故事,自己不知不觉就会拥有故事人物的特质的。 Explicit is better than implicit.(清楚优于含糊)