django中列表列表的排名和计数元素
Posted
技术标签:
【中文标题】django中列表列表的排名和计数元素【英文标题】:Rank and count elements of list of lists in django 【发布时间】:2020-06-02 19:59:05 【问题描述】:我有一个Genre
和一个User
模型。每个Genre
都有一个id
和一个name
,并且是多对多的关系,所以每个User
喜欢0个或多个Genres
。
是否有有效的查询来获取每个流派在所有用户中按后代顺序有多少喜欢?
我目前的方法有效,但效率极低,因为它必须阅读每个类型的所有用户的所有喜欢:
In [1]: genres = list(Genre.objects.values('name', 'id'))
In[2]: genres
Out[2]:
['id': 10, 'name': 'Rock',
'id': 11, 'name': 'Pop',
'id': 12, 'name': 'Hip hop',
'id': 13, 'name': 'Electronic',
'id': 14, 'name': 'Classical']
In [3]: likes_by_users = []
In [4]: users = list(User.objects.all())
In [5]: for u in users:
...: current_user_likes = []
...: likes_by_users.append(current_user_likes)
...: for lg in u.liked_genres.all():
...: current_user_likes.append(lg.pk)
In [6]: likes_by_users
Out[6]:
[[14],
[11, 12],
[11, 10, 13, 12],
[],
[13, 12, 10, 1
[10, 11]]
In [7]: counts =
In [8]: for g in genres:
...: counts[g['id']] =
...: 'name' : g['name'],
...: 'likes': 0
...:
...: for l in likes_by_users:
...: for gid in l:
...: if gid == g['id']:
...: counts[gid]['likes'] += 1
In [9]: ranking = sorted(list(counts.values()), key=lambda x : x['likes'], reverse=True)
这正是我需要的输出:
In [9]: ranking
Out[9]:
['likes': 4, 'name': 'Pop',
'likes': 3, 'name': 'Rock',
'likes': 3, 'name': 'Hip hop',
'likes': 2, 'name': 'Electronic',
'likes': 1, 'name': 'Classical']
是否有查询或其他方法可以有效地获得所需的排名?
【问题讨论】:
这是文档中的一个示例:Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')
docs.djangoproject.com/en/3.0/ref/models/querysets/#order-by
【参考方案1】:
试试这个
from django.db.models import Count
Genre.objects.annotate(likes=Count('user')).order_by('-likes')
【讨论】:
annotate
可以直接在QuerySetManager
上调用,而无需将其链接到all
感谢您的回答。由于这是多对多关系,因此没有 user
属性,而是 liked_by
属性。当调用Genre.objects.all().annotate(likes=Count('liked_by'))
时,它只会列出所有类型。当调用Genre.objects.all().annotate(likes=Count('liked_by')).order_by('-likes')
时,它会返回一个包含大量重复条目的庞大流派列表,所以我认为每个条目都代表了用户喜欢的流派。有没有办法修复这个查询,使结果类似于我的问题中的ranking
列表?
@SusyP 你可以添加你的User
(如果不是默认)和Genre
模型来提问吗?
@SusyP 在我的测试应用程序上,我的决定运行良好,没有重复条目,不确定您的情况出了什么问题。【参考方案2】:
Django 中的多对多字段由实际模型(以及表)支持。
您可以通过.through
获取支持模型:
GenreLikes = User.liked_genres.through
对于简单的多对多关系,模型将有两个字段/列,user
和 genre
在您的情况下,每个类似用户对一个实例/行。
因此,您应该能够获得您的排名
GenreLikes.objects.values('genre').annotate(likes=Count('user')).order_by('-likes')
或类似的。
【讨论】:
谢谢。这种方法有效,但我必须添加'user'
作为Count()
的参数。如果您能纠正,我将不胜感激。
@SusyP 完成交易。 :)以上是关于django中列表列表的排名和计数元素的主要内容,如果未能解决你的问题,请参考以下文章