问题描述
假设我有这个模型:
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()
现在,如果我想有效地查看相册子集中的照片子集.我这样做是这样的:
Now if I want to look at a subset of photos in a subset of albums efficiently. I do it something like this:
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 ().
This does only two queries, which is what I expect (one to get the albums, and then one like `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中过滤它们?我发誓我在文档中的某处读到它应该这样做......
Then it does a ton of queries with WHERE format = 1
! Am I doing something wrong or is django not smart enough to realise it has already fetched all the photos and can filter them in python? I swear I read somewhere in the documentation that it is supposed to do that...
推荐答案
在 Django 1.6 及更早版本中,无法避免额外的查询.prefetch_related
调用有效地为查询集中的每个相册缓存了 a.photoset.all()
的结果.但是,a.photoset.filter(format=1)
是一个不同的查询集,因此您将为每个相册生成一个额外的查询.
In Django 1.6 and earlier, it is not possible to avoid the extra queries. The prefetch_related
call effectively caches the results of a.photoset.all()
for every album in the queryset. However, a.photoset.filter(format=1)
is a different queryset, so you will generate an extra query for every album.
这在 prefetch_related
文档.
filter(format=1)
等价于 filter(spicy=True)
.
This is explained in the
prefetch_related
docs. The filter(format=1)
is equivalent to filter(spicy=True)
.
请注意,您可以通过在 python 中过滤照片来减少数量或查询:
Note that you could reduce the number or queries by filtering the photos in python instead:
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()
对象,允许您控制 prefetch_related
的行为.
In Django 1.7, there is a
Prefetch()
object that allows you to control the behaviour of 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
文档.
For more examples of how to use the
Prefetch
object, see the prefetch_related
docs.
这篇关于为什么 django 的 prefetch_related() 只适用于 all() 而不适用于 filter()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!