如何使用Django ORM实现每个Post提取2条最新Comment?
问题:如何提取每个Post的2条最新Comment?
我有如下Django模型,目标是提取每个Post的2条最新Comment,但当前尝试仅能获取1条,求解决办法。
模型代码
class Post(models.Model): author = models.ForeignKey(Author, related_name="posts", on_delete=models.CASCADE) title = models.CharField(max_length=256) text = models.TextField(blank=True, default="") created_at = models.DateTimeField(default=datetime.now, blank=True) @property def comments_number(self): return self.comments.count() def __str__(self, *args, **kwargs): return f"{self.title}" class Comment(models.Model): post = models.ForeignKey(Post, related_name="comments", on_delete=models.CASCADE) author = models.ForeignKey(Author, related_name="comment", on_delete=models.CASCADE) text = models.TextField() created_at = models.DateTimeField(default=datetime.now, blank=True)
我的尝试代码
newest = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at') recent_comments = Post.objects.annotate(newest_comments=Subquery(newest.values('text')[:2]))
调用recent_comments.values_list()时,每个Post对象仅能得到1条最新评论,不符合需求。
解决方案
问题出在Django的Subquery默认仅返回单个值,即便你切片取了前2条,它也只会提取第一个结果。要返回多条评论,可采用以下两种方法:
方法1:使用ArraySubquery(Django 3.2+支持)
直接将子查询结果转为数组,保留前2条评论内容:
from django.db.models import OuterRef, Subquery from django.db.models.functions import ArraySubquery # 筛选每个Post的前2条最新评论,提取text字段 newest_comments = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at').values('text')[:2] # 用ArraySubquery将结果转为数组 recent_comments = Post.objects.annotate( newest_comments=ArraySubquery(newest_comments) )
此时每个Post的newest_comments字段是包含最多2条评论文本的列表,调用values_list('newest_comments', flat=True)即可获取对应数据。
方法2:结合RowNumber与ArrayAgg(支持多字段提取)
如果需要获取评论的作者、创建时间等更多字段,可先给评论添加行号,筛选前2条后再聚合:
from django.db.models import OuterRef, IntegerField, ArrayAgg, F, Q from django.db.models.functions import RowNumber from django.db.models import Prefetch # 给每个Post下的评论按创建时间倒序添加行号 comment_with_row = Comment.objects.annotate( row_num=RowNumber().over( partition_by=F('post'), order_by=F('created_at').desc() ) ).filter(post=OuterRef('pk'), row_num__lte=2) # 聚合符合条件的评论字段,也可替换为author__username、created_at等 recent_comments = Post.objects.annotate( newest_comments=ArrayAgg( F('text'), filter=Q(comments__row_num__lte=2), distinct=False ) ).prefetch_related( # 可选:预加载筛选后的评论对象,方便直接操作实例 Prefetch( 'comments', queryset=Comment.objects.annotate( row_num=RowNumber().over( partition_by=F('post'), order_by=F('created_at').desc() ) ).filter(row_num__lte=2).order_by('-created_at'), to_attr='recent_comments' ) )
内容的提问来源于stack exchange,提问作者Ventre90
相关产品推荐
相关产品推荐

