Django中的连接附加条件

27 投票
3 回答
12917 浏览
提问于 2025-04-16 01:48

可以在Django的ORM中给连接语句添加额外的条件吗?

我在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的条件里呢?

如果把它加到过滤条件里,就无法获取没有关联的watchlist对象的Post对象了。

3 个回答

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

祝你编码愉快。

9

简单来说:在某些情况下,是的。

当使用通用外键(GenericForeignKey)进行左连接(LEFT JOIN)时,Django会调用 GenericRelation.get_extra_restriction,这个方法会在连接条件中添加额外的限制,具体是“content_type_id”的限制。

对于“外键”(ForeignKey),这个方法也会被调用,但返回的结果是None。

如果你能合理安排代码,以便在特定时间获取正确的限制参数,你可以在这个地方添加额外的限制到连接条件中。

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)
24

在Django 2.0版本中,可以使用FilteredRelation

Post.objects.annotate(
    t=FilteredRelation(
        'watchlist', condition=Q(watchlist__user_id=1)
).filter(t__field__in=...)

撰写回答