You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 09:01:14