Django count shared manytomany objects 并计算它们

Django count shared manytomany between objects and count them

我有两个模型,分别是 Article 和 Label。它们的简化片段如下:

class Label(models.Model):
    name = models.CharField(null=True)

class Article(models.Model):
    title = models.CharField(null=True)
    labels = models.ManyToManyField(Label, related_name='pieces', blank=True)

查看特定文章时,我想显示与正在查看的文章所应用的标签相似的文章,按与正在阅读的文章共享的标签数量排序(如 "similar articles").

我正在尝试在数据库中执行此操作,但我正在努力寻找一个查询集,该查询集将提供与我在 Python 中所做的相同的功能,方法是从数据库中提取所有文章并执行他们每个人的for循环。下面是我正在尝试执行的无效查询尝试(viewed_article 是正在查看的文章对象):

articles = Article.objects.all()\
               .annotate(
                   tags_count=Article.objects.filter(F('viewed_article.labels')
               ).count()).order_by(tags_count)

您需要使用 conditional expressions 和一个稍微复杂的查询来实现:

from django.db.models import Case, Count, IntegerField, Sum, When

current_labels = viewed_article.labels.all()

similar_articles = Article.objects.filter(labels__in=current_labels).distinct()\
    .annotate(
        tag_count=Sum(
            Case(
                When(labels__in=current_labels, then=1), 
                default=0, output_field=IntegerField()
            )
         )
    ).order_by('-tag_count')

发生的事情是:

  1. 获取与当前文章共享任何标签的所有文章。 distinct() 需要清除基础 JOIN 查询返回的重复项。

  2. 用条件表达式注释每篇文章。在这里它检查文章是否具有当前文章的每个标签,如果有则将总和加 1。结果是匹配标签的计数。

  3. 按匹配标签的数量对结果进行排序。