Django如何合并多个QuerySet并按创建时间重新排序?
Django 合并多模型 QuerySet 实现全站点近期活动流
方法1:Python 层面合并排序(小数据量首选,你的场景直接用这个就行)
你总共只需要查询15条数据,直接在Python层合并排序性能足够,实现也最简单:
- 导入
itertools.chain用于合并可迭代对象 - 给每类数据添加类型标记,方便后续渲染区分活动类型
- 合并后统一按
created字段倒序排序
代码实现:
from itertools import chain # 查询三类最新数据,分别标记活动类型 groups = Group.objects.all().order_by('-created')[:5] for group in groups: group.activity_type = "group" posts = Post.objects.all().order_by('-created')[:5] for post in posts: post.activity_type = "post" comments = Comment.objects.all().order_by('-created')[:5] for comment in comments: comment.activity_type = "comment" # 合并后按创建时间倒序排列 activity_list = list(chain(groups, posts, comments)) activity_list.sort(key=lambda item: item.created, reverse=True) # 按需取前N条展示,比如取前10条最新活动 # activity_list = activity_list[:10]
模板渲染示例:
<ul class="activity-list"> {% for activity in activity_list %} <li> {% if activity.activity_type == "group" %} {{ activity.creator.username }} 创建了群组「{{ activity.name }}」 {% elif activity.activity_type == "post" %} {{ activity.author.username }} 在「{{ activity.group.name }}」群组发布了帖子 {% elif activity.activity_type == "comment" %} {{ activity.author.username }} 在「{{ activity.post.group.name }}」群组发布了评论 {% endif %} </li> {% endfor %} </ul>
方法2:数据库层面 UNION 联合查询(大数据量场景用)
如果后续活动数据量变大,可以用Django ORM的union方法让排序逻辑在数据库层面完成,性能更高,要求三个查询返回的字段结构完全一致:
from django.db.models import F, Value, CharField # 统一查询字段结构,用annotate补全相同字段 groups = Group.objects.annotate( activity_type=Value("group", output_field=CharField()), operator=F("creator__username"), group_name=F("name"), extra_info=Value("", output_field=CharField()) ).values("created", "activity_type", "operator", "group_name", "extra_info")[:5] posts = Post.objects.annotate( activity_type=Value("post", output_field=CharField()), operator=F("author__username"), group_name=F("group__name"), extra_info=F("title") ).values("created", "activity_type", "operator", "group_name", "extra_info")[:5] comments = Comment.objects.annotate( activity_type=Value("comment", output_field=CharField()), operator=F("author__username"), group_name=F("post__group__name"), extra_info=F("content")[:20] ).values("created", "activity_type", "operator", "group_name", "extra_info")[:5] # 联合后直接在数据库层面排序 activity_list = groups.union(posts, comments).order_by("-created")[:10]
内容的提问来源于stack exchange,提问作者Ashik Rahman
相关产品推荐
相关产品推荐

