Django LEFT JOIN,有效吗?

时间:2012-07-31 18:00:23

标签: sql django left-join

假设我有这个模型:

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中它是可能的。

3 个答案:

答案 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()