获取最常用的多对多字段

时间:2018-07-22 11:28:13

标签: sql django database django-models django-queryset

我有以下型号。

class Tag(models.Model):
    name = models.CharField(max_length=30)
    # and other fields ...

class Book(models.Model):
    name = models.CharField(max_length=140)
    tags = models.ManyToManyField(Tag, blank=True)
    # and other fields 

class Article(models.Model):
    name = models.CharField(max_length=140)
    tags = models.ManyToManyField(Tag, blank=True)

很少有其他模型具有“ ManyToMany”字段的标签。我想获取最常用的标签对象的列表。我尝试从每个模型中过滤最常用的标签,然后从每个模型中获取前十名,并将它们与其他前十名组合。我认为应该从“标签”模型本身中做些什么来找到最常用的标签实例。

除了我的方法之外,有什么方法可以找到最常用的标签实例?任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:0)

书籍模型中10个最常用标签的列表:

tags_of_book = Tag.objects.all().annotate(num_book = Count('book')).order_by('-num_book')[:10]

文章模型中10个最常用标签的列表:

tags_of_article = Tag.objects.all().annotate(num_article = Count('article')).order_by('-num_article')[:10]

答案 1 :(得分:0)

您可以使用annotate()使标签按在Article中使用的时间排序(对Book可以这样做):

Tag.objects.all().annotate(
    nb_articles=Count("article_set"),
).order_by("-nb_articles")

但是,要在ArticleBook中获得最常用的标签,可能需要更多查询或可能使用Subqueries(请参阅doc)的更高级的

答案 2 :(得分:0)

假设您要让Book的前10个标签使用,然后可以这样查询:

from django.db.models import Count

Tag.objects.annotate(
    nused=Count('book')
).order_by('-nused')[:10]

因此,我们根据每个标签的相关书籍数量在数据库中查询Tag所订购的商品。

我们可以使用多个计数,但是通过在一个查询中使用它们,通常会产生昂贵的查询:在这种情况下,您将JOIN使用所有这些相关模型,因此,时间复杂度查询通常会在模型数量上呈指数增长。尽管有些数据库管理员可能发现这些子查询是“ 独立”子查询,但根据我的经验,流行的子查询通常不会。因此,我们最好在这里使用多个查询:每个相关模型一个。

因此,现在我们首先需要找出相关模型。幸运的是,Django为此提供了一些实用程序功能。每个模型类都有一个._meta对象,该对象存储有关模型的信息。这些属性之一是.fields_map,它返回一个字典,该字典将关系的名称映射到关系对象。

我们可以使用它来枚举关系,因此对于每个关系都使用查询:

from collections import Counter
from django.db.models import Count

cntr = Counter()
for relation in Tag._meta.fields_map:
    cntr.update(
        {
            tg: tg.nr
            for tg in Tag.objects.annotate(nr=Count(relation)).order_by('nr')[:10]
        }
    )

最后,我们将有一个Counter,其中包含这些标签的总出现次数。但是请注意,由于我们每次都将数字限制为10。

然后我们可以通过从柜台获得最常用的标签来获取最受欢迎的标签:

from operator import itemgetter

my_tags = map(itemgetter(0), ca.most_common(10))

.most_common(10)将生成前10个标记(通过将每种关系中最常见的标记求和),并返回2元组的列表:每个元组都包含Tag实例,以及使用次数。通过使用map(itemgetter(0), ...),我们仅获得Tag。但是您可能也对数字感兴趣。

为什么限制每个 关系可能不是一个好主意...

这不是不是,这意味着我们本身拥有最频繁的标签。确实,假设某个标签是BookArticle的第11个最受欢迎的标签,那么它仍然可能是整体上最受欢迎的标签,因为有可能排名前10位BookArticle完全不同。还是一个小例子:

Top Books    Top Articles
1. A (10)    1. D (12)
2. B (8)     2. E (8)
3. C (7)     3. C (7)

如果使用上述方法生成前2名,则我们将错过实际发生次数最多(共C次)的14

我们可以通过简单地始终计数所有Tag来解决此问题,从而消除[:10]的限制:

from collections import Counter
from django.db.models import Count

cntr = Counter()
for relation in Tag._meta.fields_map:
    cntr.update(
        {
            tg: tg.nr
            for tg in Tag.objects.annotate(nr=Count(relation)).order_by('nr')
        }
    )