Django中关于join的附加条件

27

在Django ORM中,是否可以为JOIN语句添加其他条件?

我需要的是SQL中的:

'SELECT "post"."id", COUNT("watchlist"."id") FROM "post" 
 LEFT OUTER JOIN "watchlist" 
    ON ("post"."id" = "watchlist"."post_id" AND "watchlist"."user_id" = 1) 
 WHERE "post"."id" = 123  GROUP BY …
在 Django 中,大部分都是这样的。
Post.objects.annotate(Count('watchinglist')).get(pk=123)

但是我该如何使用django ORM将AND "watchlist"."user_id" = …添加到JOIN条件中?

将其添加到过滤器中无法获取没有关联对象的Post对象。


1
很好的问题,不确定为什么会被视为标记问题的重复。这个问题是指向关系添加额外过滤器,而不仅仅是开始执行一个过滤器。 - Bigbob556677
1
对的,这与被标记为重复的其他问题不同。 - Johan Dahlin
3个回答

24
在Django v2.0中使用FilteredRelation
Post.objects.annotate(
    t=FilteredRelation(
        'watchlist', condition=Q(watchlist__user_id=1)
).filter(t__field__in=...)

1
这是针对较新版本Django的最佳直接回答。但不幸的是,FilteredRelation在多个关系之间无法工作,因此目前有些不完整。 - getup8
我同意 @getup8 的观点,FilteredRelation 是一个半成品功能,你不能通过属性访问获取 t 的值,它只能与 valuesvalues_list 一起使用。 - kakarukeys
1
请注意,在<3.0版本中存在一个错误,如果关系查询结果为空,则会出现问题。https://code.djangoproject.com/ticket/29810 我找到的唯一解决方法是使用更新的Django版本。 - Justin Meiners
我已经在Django 3.2中尝试过这个方法,对于我的目的(一对多,不是一些复杂的查询),使用FilteredRelation通过添加一个AND条件进行连接,然后在WHERE子句上添加一个过滤器似乎可以很好地工作。到目前为止,这似乎是最好的答案(虽然不确定其他人提到的cavecats是否适用于所有情况)。 - babis21

9

简短回答:在某些情况下 - 是的。

当使用GenericForeignKey构建LEFT JOIN时,Django调用GenericRelation.get_extra_restriction,它会在ON子句中添加带有“content_type_id”限制的额外条件。

对于“ForeignKey”,该方法也被调用,但返回值为None。

如果您成功地组织了代码以在特定时间获取适当的限制参数,则可以使用此处来将额外的限制条件放入ON子句中。

class UserForeignKey(models.ForeignKey):

    def get_extra_restriction(self, where_class, alias, related_alias):
        field = self.model._meta.get_field('user')
        cond = where_class()
        # Here is a hack to get custom condition parameters
        value = SomeContextManager.get_needed_value()

        lookup = field.get_lookup('exact')(field.get_col(related_alias), value)
        cond.add(lookup, 'AND')
        return cond

class WatchList(models.Model):

    user = UserForeignKey(User)

2
Post.objects.annotate(Count('watchinglist')).filter(pk=123).extra(where=['"watchlist"."user_id" = 1'])

愉快的编码。


5
看起来它实际上和filter()做的事情是一样的。例如,在WHERE中添加一个参数,而不是在ON中添加一个参数,因此如果没有观看列表对象,则会提供空结果。 - HoverHell
但事实上是个好主意。我错过了 Count 的“extra”参数。看起来这个可行。 - HoverHell
...或者也许不是。在那里的“extra”似乎有着略微不同的目的。 - HoverHell
1
EXTRA在ON语句中没有添加任何内容,但它会在WHERE语句中添加内容。我使用它来通过在条件中简单地添加连接表(T4、T5)的名称来修改JOIN结果。这并不美观,但我认为它比使用自定义SQL更好。 - Vladimir Mikhaylovskiy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接