我有下列型号。
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”模型本身找到最常用的标记实例。
除了我的方法之外,还有什么方法可以找到最常用的标记实例吗?任何帮助都将不胜感激。
最佳答案
假设您想要Book
s使用的前10个标记,那么您可以如下查询:
from django.db.models import Count
Tag.objects.annotate(
nused=Count('book')
).order_by('-nused')[:10]
因此,我们根据每个标签的相关书籍数量查询数据库中的
Tag
s。我们可以使用多个计数,但是在一个查询中使用它们,这通常会产生一个昂贵的查询:在这种情况下,您将在所有这些相关模型上使用
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
s。但是您可能也对数字感兴趣。为什么限制每段关系可能是个坏主意…
这并不意味着我们本身就拥有最频繁的标签。实际上,假设一个标签是
Book
s和Article
s的第11个最流行的标签,那么这个标签仍然可以是总体上最流行的标签,因为Book
s和Article
s的前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
次)。我们可以通过始终计算所有
Tag
s来解决此问题,从而消除[: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/