Django count在对象之间共享多个并计算它们

时间:2016-07-22 16:32:19

标签: django django-queryset

我有两个模型,名为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)

在查看特定文章时,我希望显示与正在查看的文章具有相似标签的文章,按与正在阅读的文章共享的标签数量排序(例如"类似文章& #34;。)

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

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

1 个答案:

答案 0 :(得分:2)

您需要使用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. 按匹配标签的数量订购结果。