我有下列型号。

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)

而且很少有其他型号有像manytomynield这样的标签。我想得到最常用标记对象的列表。我尝试从每个模型中筛选最常用的标记,然后从每个模型中筛选出前十个,并将它们与其他前十个组合起来。我认为应该从“tag”模型本身找到最常用的标记实例。
除了我的方法之外,还有什么方法可以找到最常用的标记实例吗?任何帮助都将不胜感激。

最佳答案

假设您想要Books使用的前10个标记,那么您可以如下查询:

from django.db.models import Count

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

因此,我们根据每个标签的相关书籍数量查询数据库中的Tags。
我们可以使用多个计数,但是在一个查询中使用它们,这通常会产生一个昂贵的查询:在这种情况下,您将在所有这些相关模型上使用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), ...),我们只得到Tags。但是您可能也对数字感兴趣。
为什么限制每段关系可能是个坏主意…
这并不意味着我们本身就拥有最频繁的标签。实际上,假设一个标签是Books和Articles的第11个最流行的标签,那么这个标签仍然可以是总体上最流行的标签,因为Books和Articles的前10个标签可能是完全不同的。或者举个小例子:
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次)。
我们可以通过始终计算所有Tags来解决此问题,从而消除[: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')
        }
    )

关于sql - 获得最常用的多对多字段,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51464619/

10-12 15:41