Django 对多对多进行过滤,并具有计数

Django filter on many to many with having count

提问人:Guillaume Cisco 提问时间:7/24/2023 最后编辑:Guillaume Cisco 更新时间:7/24/2023 访问量:32

问:

经过多次尝试,我无法将这种 sql 查询转换为 django 过滤。 基本上,它检索具有至少 x 个选定标签(和其他过滤器)的所有用户。 在两个标签的情况下,用户需要同时拥有两个标签,我不想检索具有至少一个标签的用户。

以下是我的定义:

class User(models.Model):
    name = models.CharField(max_length=64)
    tags = models.ManyToManyField(Tag)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
class Tag(models.Model):
    name = models.CharField(max_length=128)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

SQL 查询将如下所示:

SELECT user.id
FROM user
INNER JOIN "user_tags" ON ("user"."id" = "user_tags"."user_id")
AND "user_tags"."tag_id" in (8, 163) group by "user"."id", user_tags.user_id having count(*) = 2
ORDER BY user.id DESC LIMIT 100;

如您所见,该部分是在创建的 m2m 表上完成的,并且标签的过滤也having count(*)user_tags"user_tags"."tag_id" in (8, 163)

它可以通过 django 方式实现吗? 我所有的尝试都使用 和 表,从未创建过表。usertab

谢谢

编辑:将 @preeti-y 作为正确答案传递,因为看起来这是要走的路。 但是出于我的具体情况和性能原因,我直接使用了一种方法,允许我正确地将其与其他过滤器一起传递。RawSQL

Q(id__in=RawSQL(
                    '''SELECT U0.id FROM "user" U0 
                    INNER JOIN "user_tags" U1 ON (U0."id" = U1."user_id") 
                    WHERE U1."tag_id" IN %s
                    GROUP BY U0."id" 
                    HAVING COUNT(U1."user_id") = %s''', (tuple(value), len(value),)))
sql 姜戈 多对多 psql 拥有

评论


答:

0赞 Preeti Y. 7/24/2023 #1

根据你的查询,在 Django 的方式中,你可以像这样在注解中使用参数:Count()filter

User.objects.annotate(
  matched_tag_count=Count('tags', filter=Q(tags__in=[8, 163]))
).filter(matched_tag_count=2)

评论

0赞 Guillaume Cisco 7/24/2023
这可行,但使用其他过滤器的性能很糟糕。我最后使用了 RawSQL......