14

我正在使用 Django 的内置用户模型,并有一个自定义 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 对象。或者,我可以选择所有用户对象并为每个对象运行一个查询,但这会大大降低效率。

4

5 回答 5

16

要获得一个LEFT OUTER JOIN,你可以去:

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

Django 使用foo__isnull=True来指导它生成一个LEFT OUTER JOIN. 如果没有 filter 参数,Giving foo__isnull=Falseto 会生成一个。INNER JOIN

于 2018-01-19T02:21:15.293 回答
5

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

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

但是,您似乎需要通过指定 in或附加注释来明确要求filtered_foo您要使用的字段。values或者,您还可以聚合filtered_foo按用户分组的字段。

于 2020-09-22T14:25:03.397 回答
0

如果您希望 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,所以我认为这是一种合理的方法。)

于 2013-03-25T20:46:25.900 回答
0

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

from django.db.models import Prefetch


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

<related_name> = 'foo' 如果:

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

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

于 2021-02-04T21:09:19.797 回答
0

它很笨重,但应该这样做:

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

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

于 2015-09-15T13:46:12.500 回答