ManyToMany 关系的查询集按列表中的匹配数排序

Queryset for ManyToMany relation ordered by number of match from a list

我有两个模型,实体和标签具有多对多关系:

class Tag(models.Model):
    name = models.CharField(max_length=128)

class Entity(models.Model):
    name              = models.CharField(max_length=128)
    tags              = models.ManyToManyField(Tag)

在一个视图中,我有一个标签 ID 列表,并希望获得至少一个标签的前 10 名实体,这些标签按它们与我的列表共有的标签数量排序。

我目前正在通过 'tags__in' 完成 "at least one" 部分,并在我的查询集末尾添加一个“.distinct()”。这是正确的方法吗?

我看到 annotate 中有一个 'Count' 可以有一些参数,有没有办法只指定 id 在我的列表中? 如果不是,我是否需要像这样(来自文档)进行 SQL 查询?我担心我会失去一些性能。

Blog.objects.extra(
    select={
        'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id'
    },
)

如果我正确理解了你的问题,我认为这个查询应该可以满足你的要求(我使用的是 django 3.0):

from django.db.models import Count, Q

Entity.objects.annotate(tag_count=Count("tags", filter=Q(tags__id__in=tag_ids))).filter(
    tag_count__gt=0
).order_by("-tag_count")[:10]

生成SQL(我使用的是 postgres):

SELECT "people_entity"."id",
       "people_entity"."name",
       COUNT("people_entity_tags"."tag_id") FILTER (WHERE "people_entity_tags"."tag_id" IN (1, 4, 5, 8)) AS "tag_count"
  FROM "people_entity"
  LEFT OUTER JOIN "people_entity_tags"
    ON ("people_entity"."id" = "people_entity_tags"."entity_id")
 GROUP BY "people_entity"."id"
HAVING COUNT("people_entity_tags"."tag_id") FILTER (WHERE ("people_entity_tags"."tag_id" IN (1, 4, 5, 8))) > 0
 ORDER BY "tag_count" DESC
 LIMIT 10

小编辑:在导入中添加了 Q