说我有一个模型:
class Foo(models.Model):
...
Run Code Online (Sandbox Code Playgroud)
和另一个基本上为每个用户提供以下信息的模型Foo
:
class UserFoo(models.Model):
user = models.ForeignKey(User)
foo = models.ForeignKey(Foo)
...
class Meta:
unique_together = ("user", "foo")
Run Code Online (Sandbox Code Playgroud)
我想生成一个Foo
s的查询集,但注释与(可选)相关的UserFoo
基于user=request.user
.
所以它实际上是一个 LEFT OUTER JOIN on (foo.id = userfoo.foo_id AND userfoo.user_id = ...)
Bri*_*ner 16
raw
可能看起来像的解决方案
foos = Foo.objects.raw("SELECT foo.* FROM foo LEFT OUTER JOIN userfoo ON (foo.id = userfoo.foo_id AND foo.user_id = %s)", [request.user.id])
Run Code Online (Sandbox Code Playgroud)
您需要修改SELECT
以包含额外的字段,userfoo
这些字段将注释到查询Foo
集中的结果实例.
小智 9
注意:此方法在Django 1.6+中不起作用.正如下面tcarobruce的评论中所解释的那样,该
promote
论点被删除作为#19849票的一部分:ORM清理.
Django没有提供完全内置的方法来实现这一点,但是构建一个完全原始的查询并不是必需的.(此方法不用于选择工作*
从UserFoo
,所以我使用.comment
作为一个例子字段从以包括UserFoo
).
将QuerySet.extra() method
允许我们添加条款,SELECT和WHERE我们的查询的WHERE子句.我们使用它来UserFoo
在结果中包含表中的字段,并将UserFoo
匹配限制为当前用户.
results = Foo.objects.extra(
select={"user_comment": "UserFoo.comment"},
where=["(UserFoo.user_id IS NULL OR UserFoo.user_id = %s)"],
params=[request.user.id]
)
Run Code Online (Sandbox Code Playgroud)
此查询仍需要该UserFoo
表.可以使用.extras(tables=...)
获取隐式INNER JOIN,但对于OUTER JOIN,我们需要自己修改内部查询对象.
connection = (
UserFoo._meta.db_table, User._meta.db_table, # JOIN these tables
"user_id", "id", # on these fields
)
results.query.join( # modify the query
connection, # with this table connection
promote=True, # as LEFT OUTER JOIN
)
Run Code Online (Sandbox Code Playgroud)
我们现在可以评估结果.每个实例都有一个.user_comment
包含值的属性UserFoo
,或者None
如果它不存在.
print results[0].user_comment
Run Code Online (Sandbox Code Playgroud)
(感谢Colin Copeland撰写的这篇博客文章,向我展示了如何进行OUTER JOIN.)
这个答案可能不是你正在寻找的,但是因为它是谷歌搜索"django annotate outer join"时的第一个结果,所以我将在这里发布.
注意:在Djang 1.7上测试过
假设您有以下型号
class User(models.Model):
name = models.CharField()
class EarnedPoints(models.Model):
points = models.PositiveIntegerField()
user = models.ForgeinKey(User)
Run Code Online (Sandbox Code Playgroud)
要获得总用户点数,您可以执行类似的操作
User.objects.annotate(points=Sum("earned_points__points"))
Run Code Online (Sandbox Code Playgroud)
这将工作,但它不会返回没有积分的用户,这里我们需要外连接没有任何直接黑客或原始SQL
你可以通过这样做来实现这一目标
users_with_points = User.objects.annotate(points=Sum("earned_points__points"))
result = users_with_points | User.objects.exclude(pk__in=users_with_points)
Run Code Online (Sandbox Code Playgroud)
这将被转换为OUTER LEFT JOIN,并且将返回所有用户.没有积分的用户将None
在其points属性中拥有值.
希望有所帮助