Django QuerySet两层关联注解:按关联书籍阅读量排序Person列表
按关联书籍阅读次数排序Person列表的解决方案
嘿,这个需求其实挺常见的,核心就是要把Person→PersonBookRole→Book→Reading这条关联链打通,然后统计每个Person对应的总阅读次数就行。先给你理清楚步骤,再上代码:
先明确模型关联逻辑
假设你的模型结构大概是这样的(方便对应代码理解):
class Person(models.Model): name = models.CharField(max_length=100) class Book(models.Model): title = models.CharField(max_length=200) # 用through模型关联Person,记录角色 people = models.ManyToManyField(Person, through='PersonBookRole') class PersonBookRole(models.Model): person = models.ForeignKey(Person, on_delete=models.CASCADE) book = models.ForeignKey(Book, on_delete=models.CASCADE) role = models.CharField(max_length=50) # 比如"Author"、"Editor" class Reading(models.Model): book = models.ForeignKey(Book, on_delete=models.CASCADE) # 其他阅读字段,比如阅读时间、读者ID等
基础查询:统计所有关联书籍的总阅读次数
要实现按阅读次数排序,我们可以用Django的annotate结合Count来做,关键是要正确跨表关联:
from django.db.models import Count # 获取按关联书籍总阅读次数降序排列的Person列表 sorted_people = Person.objects.annotate( # 链式关联:Person→PersonBookRole→Book→Reading,统计唯一的Reading记录数 total_readings=Count('personbookrole__book__reading', distinct=True) ).order_by('-total_readings')
为什么加distinct=True?
因为一个Person可能和同一本书有多个角色(比如既是作者又是编辑),如果不加distinct,同一本书的阅读记录会被重复统计。加上之后就能确保每一条Reading只被计算一次。
进阶:按特定角色统计阅读次数
如果只想统计某类角色(比如只算作者关联的书籍阅读次数),可以先筛选角色再做统计:
from django.db.models import Count # 只统计作为"Author"的Person,按其书籍阅读次数排序 author_sorted_people = Person.objects.filter( personbookrole__role='Author' ).annotate( total_author_readings=Count('personbookrole__book__reading', distinct=True) ).order_by('-total_author_readings')
额外技巧:排除无阅读记录的Person
如果不想展示那些没有任何书籍被阅读过的Person,可以在查询末尾加上过滤条件:
sorted_people = Person.objects.annotate( total_readings=Count('personbookrole__book__reading', distinct=True) ).filter(total_readings__gt=0).order_by('-total_readings')
这样就能得到完全符合你需求的排序结果啦!
内容的提问来源于stack exchange,提问作者Phil Gyford
相关产品推荐
相关产品推荐

