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

Django DRF中如何通过Serializer高效获取对象的同级兄弟记录

Django DRF 返回相邻兄弟记录优化方案

方案1:序列化器内嵌相邻字段(无需额外action)

直接在原有业务的模型序列化器中新增自定义的上/下一篇字段,请求单条记录详情时自动返回,完全不需要额外开发独立的action接口:

from rest_framework import serializers
from .models import Service

class ServiceSerializer(serializers.ModelSerializer):
    # 自定义上下篇字段
    prev = serializers.SerializerMethodField()
    next = serializers.SerializerMethodField()

    class Meta:
        model = Service
        # 新增prev、next到你原本的字段列表即可
        fields = ["id", "title", "parent", "content", "prev", "next"]

    def get_prev(self, obj):
        # 取同父级下id小于当前记录的最大id对应记录
        prev_obj = Service.objects.filter(parent=obj.parent, id__lt=obj.id).order_by("-id").first()
        if prev_obj:
            # 仅返回跳转必要字段即可,降低序列化开销
            return {"id": prev_obj.id, "title": prev_obj.title}
        return None

    def get_next(self, obj):
        # 取同父级下id大于当前记录的最小id对应记录
        next_obj = Service.objects.filter(parent=obj.parent, id__gt=obj.id).order_by("id").first()
        if next_obj:
            return {"id": next_obj.id, "title": next_obj.title}
        return None

配置后原有详情接口不需要做任何修改,返回结果会自动携带上下篇信息。

方案2:窗口函数优化查询性能

如果对接口响应性能要求较高,可以使用Django支持的Lag/Lead窗口函数,一次查询即可拿到当前记录和相邻记录的id,避免多次数据库查询:

from django.db.models import Window, F
from django.db.models.functions import Lag, Lead
from rest_framework.response import Response

# 重写视图的retrieve方法
def retrieve(self, request, *args, **kwargs):
    # 给查询集增加上下篇id注解,按父级分组、id排序取前后行id
    queryset = self.queryset.annotate(
        prev_id=Window(
            expression=Lag("id"),
            partition_by=F("parent"),
            order_by=F("id").asc()
        ),
        next_id=Window(
            expression=Lead("id"),
            partition_by=F("parent"),
            order_by=F("id").asc()
        )
    )
    instance = queryset.get(pk=kwargs["pk"])
    base_data = self.get_serializer(instance).data
    # 若仅需要上下篇id,直接将prev_id、next_id插入返回结果即可,全程仅1次数据库查询
    base_data["prev_id"] = instance.prev_id
    base_data["next_id"] = instance.next_id

    # 若需要返回上下篇完整信息,多1次批量查询即可,比原方案少1次查询
    related_ids = list(filter(None, [instance.prev_id, instance.next_id]))
    if related_ids:
        related_map = {obj.id: self.get_serializer(obj).data for obj in self.queryset.filter(id__in=related_ids)}
        base_data["prev"] = related_map.get(instance.prev_id)
        base_data["next"] = related_map.get(instance.next_id)
    else:
        base_data["prev"] = base_data["next"] = None
    return Response(base_data)

原有代码优化建议

如果你需要保留独立的action接口,可以做两处优化:

  • 冗余条件删除:过滤id__lt/id__gt后已经排除了当前记录的id,不需要再加exclude(pk=current_service.id)条件
  • 修复next查询bug:你原有代码中查询next时使用order_by("-id")会拿到同父级下最大的id,不是相邻的下一篇,需要改为order_by("id")

内容的提问来源于stack exchange,提问作者Spirconi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 01:24:03