Django使用过滤器离开了外连接

时间:2013-03-25 18:43:07

标签: sql django join

我使用Django的内置用户模型,并拥有一个带有ForeignKey to User的自定义Foo对象。我希望选择所有符合某些约束的User对象和所有Foo对象,如下所示:

SELECT * from auth_user LEFT OUTER JOIN "foo" ON
(auth_user.id = foo.id AND <other criteria here>)

我应该如何在Django中实现这一目标?到目前为止,我已经尝试过:

User.objects.filter(foo__<criteria>)

但是生成与此类似的SQL:

SELECT * from auth_user LEFT OUTER JOIN "foo" ON
(auth_user.id = foo.id) WHERE <other criteria here>

并且仅返回具有符合条件的Foo对象的User对象。或者,我可以选择所有用户对象并为每个对象运行查询,但效率会大大降低。

5 个答案:

答案 0 :(得分:1)

如果您希望Django获取所有User个对象以及与用户对象相关的所有Foo个对象,那么您将使用select_related()

User.objects.all().select_related('foo')

但是在这里你不希望所有与用户对象相关的Foo个对象,你只需要它们的子集满足你的标准。我不知道告诉Django在单个查询集中这样做的方法。但是你可以做的就是分别选择并在Python中进行连接:

# Map from user id to corresponding Foo satisfying <criteria>, if any.
foos = {foo.user_id: foo for foo in 
        Foo.objects.filter(user__isnull = False, <criteria>)}
for user in User.objects.all():
    foo = foos.get(user.id)
    # ...

(这不会再进行数据库工作或传输比LEFT OUTER JOIN更多的数据了,所以我认为这是一种合理的方法。)

答案 1 :(得分:1)

这很笨重但是应该这样做:

User.objects.raw('SELECT auth_user.* from auth_user LEFT OUTER JOIN '
                 '"foo" ON (auth_user.id = foo.id AND '
                 '<other criteria here>)')

您可以通过将返回的用户对象添加到选择的左侧来展开额外属性。

答案 2 :(得分:1)

要获得LEFT OUTER JOIN你可以去:

User.objects.select_related('foo').filter(Q(foo__isnull=True) | Q(<other criteria here>))

Django使用foo__isnull=True来指示它生成LEFT OUTER JOIN。如果没有过滤器参数,请foo__isnull=False生成INNER JOIN

答案 3 :(得分:0)

Django 2.0引入了FilteredRelation objects,它可以产生与您提到的LEFT OUTER JOIN查询几乎完全相同的代码,

User.objects.annotate(
    filtered_foo=FilteredRelation('foo', condition=Q(foo_<criteria>))
).values(...) # e.g. 'user__id', 'filtered_foo__id'

但是,您似乎需要通过在filtered_foo中进行指定或使用其他注释来显式地询问要使用的values字段。另外,您也可以汇总由用户分组的filtered_foo字段。

答案 4 :(得分:0)

虽然有很好的答案可以满足一个人的需求,但最好的做法是

from django.db.models import Prefetch


User.objects.all().prefetch_related(Prefetch(<related_name>, queryset=Foo.objects.filter(<criteria>)))

= 'foo' 如果:

class Foo(models.Model):
    user = models.ForeignKey(User, related_name='foo')

这将返回与具有给定过滤条件的用户相关的所有用户和 foo 对象。