假设我有这个模型:
class PhotoAlbum(models.Model):
title = models.CharField(max_length=128)
author = models.CharField(max_length=128)
class Photo(models.Model):
album = models.ForeignKey('PhotoAlbum')
我想做这个查询:“找到10个名字以'The'开头的专辑,然后给我这些专辑中的所有照片。”
在SQL中,我可以这样做:
SELECT * FROM
(SELECT * FROM photoalbum WHERE title LIKE 'The%' LIMIT 10) AS selected_albums
LEFT JOIN photo ON photo.album_id = selected_albums.id
我的问题是,我怎样才能在Django中这样做? (没有触及每个专辑的查询!) 我认为这是一个相当普遍的要求,我不敢相信没有办法做到这一点。< / p>
如果没有Django-ey方式,我会满足于“如何使用原始SQL在Django中实现它?”。
以下是一些不起作用的事情:
select_related()
;这是前进 ForeignKey
关系,这是倒退。prefetch_related()
;也用于前进关系。编辑:实际上这个 工作!至少为ForeignKey
s。的一个级别。PhotoAlbum.photo_set
;触发每个专辑的查询。我最接近的是:
专辑= PhotoAlbum.objects.all()[:10] photos = Photo.objects.filter(album__in = albums)
但遗憾的是它并不适用于MySQL,而且我被告知使用LEFT JOIN
比使用WHERE ... IN (SELECT ...)
类型查询更好。
我找到了3 year old mailing list post about the problem。其中没有解决方案。
A 6 year old bug report saying they won't fix it。除了“那不是它的工作方式”之外没有其他理由。显然,在RoR中它是可能的。
答案 0 :(得分:4)
在Django 1.4+中,您可以使用prefetch_related
:
PhotoAlbum.objects.filter(title__startswith='The').prefetch_related('photo')[:10]
在较小的版本中,请尝试django-batch-select。
<强>更新强>
对不起。我主要是在1.3上,所以我不使用prefetch_related
。在每个其他查询类型中,您不包含_set
附录,但Django显然在此处破坏了约定。如果您使用prefetch_related('photo_set')
,它将起作用。
如果您需要获取多个内容,可以像使用select_related
一样列出字段,即:
prefetch_related('something', 'something_else', 'foo')
但请密切注意文档中的这一部分:
还要记住,与QuerySets一样,任何暗示不同数据库查询的后续链接方法都将忽略以前缓存的结果,并使用新的数据库查询检索数据。所以,如果你写下面的内容:
>>> pizzas = Pizza.objects.prefetch_related('toppings') >>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
...那么pizza.toppings.all()已被预取的事实对你没有帮助 - 实际上它会伤害性能,因为你已经完成了一个你没有使用过的数据库查询。所以请谨慎使用此功能!
答案 1 :(得分:1)
这将有效并且只进行两次查询。 prefetch_related适用于反向FK,实际上它是为它创建的:
for album in PhotoAlbum.objects.filter(title__startswith='The').prefetch_related('photo_set')[:10]:
print album.photo_set.all()
答案 2 :(得分:0)
您不想在所有相册上触发照片集,是吗?只是指定的?
for p in PhotoAlbum.objects.filter(title__startswith='The'):
p.photo_set.all()