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

如何在Django REST Framework中返回自引用模型的嵌套层级结构?

如何在Django REST Framework中实现未知层级的树形结构序列化(不修改视图代码)

问题背景

我们正在开发一个Django REST Framework项目,连接存储多层级树形结构数据的PostgreSQL数据库。需要提供一个GET请求端点,无参数传入时返回完整的嵌套树形结构(包含父节点、子节点、孙节点等),且不能修改现有视图代码。

示例数据

数据库中地区数据结构如下(层级可扩展,深度未知):

idregionparent_region_id
1worldNULL
2europe1
3asia1
4africa1
5belgium2
6germany2
7spain2
8japan3
9indonesia3
10vietnam3
11tanzania4
12egypt4
13senegal4

目标输出

GET请求/region时需返回如下嵌套JSON结构:

{
   "id":1,
   "region":"world",
   "children":[
      {
         "id":2,
         "region":"europe",
         "children":[
            {
               "id":5,
               "region":"belgium"
            },
            {
               "id":6,
               "region":"germany"
            },
            {
               "id":7,
               "region":"spain"
            }
         ]
      },
      {
         "id":3,
         "region":"asia",
         "children":[
            {
               "id":8,
               "region":"japan"
            },
            {
               "id":9,
               "region":"indonesia"
            },
            {
               "id":10,
               "region":"vietnam"
            }
         ]
      },
      {
         "id":4,
         "region":"africa",
         "children":[
            {
               "id":11,
               "region":"tanzania"
            },
            {
               "id":12,
               "region":"egypt"
            },
            {
               "id":13,
               "region":"senegal"
            }
         ]
      }
   ]
}

已尝试代码及问题

现有代码返回的子节点仅为ID列表,而非嵌套结构,且递归方案存在超时或序列化错误:

# Models.py
class HierarchyData(models.Model):
    region = models.CharField(max_length=100, null=False, default=None)
    parent = models.ForeignKey("self", models.DO_NOTHING, null=True, blank=True, db_column='parent', related_name="children")

# Serializers.py
class HeirarchyDataSerializer(serializers.ModelSerializer):
    class Meta:
        model = HierarchyData
        fields = ["id","region", "children"]

# Views.py
class ListHierarchyData(generics.ListAPIView):
    queryset = HierarchyData.objects.all()
    serializer_class = HeirarchyDataSerializer
    permission_classes = [IsAuthenticated]

错误返回结果:

{
    "id": 1,
    "region": "world",
    "children": [2,3,4]
}

解决方案

1. 修正模型并优化查询(关键)

修改模型,添加自定义Manager实现根节点过滤和递归预取所有子节点,避免N+1查询问题,同时匹配数据库实际列名:

# Models.py
from django.db import models
from django.db.models import Prefetch

def recursive_prefetch():
    # 递归预取所有层级的子节点,彻底解决N+1查询
    return Prefetch(
        'children',
        queryset=HierarchyData.objects.prefetch_related(recursive_prefetch())
    )

class HierarchyDataManager(models.Manager):
    def get_queryset(self):
        # 仅返回根节点(parent为NULL的节点),并预取所有子节点
        return super().get_queryset()\
            .filter(parent__isnull=True)\
            .prefetch_related(recursive_prefetch())

class HierarchyData(models.Model):
    region = models.CharField(max_length=100, null=False)
    parent = models.ForeignKey(
        "self", 
        on_delete=models.DO_NOTHING, 
        null=True, 
        blank=True, 
        db_column='parent_region_id',  # 匹配数据库实际列名
        related_name="children"
    )

    objects = HierarchyDataManager()

    class Meta:
        db_table = '你的实际表名'  # 替换为数据库中地区表的名称

2. 修改序列化器实现递归嵌套

更新序列化器,使用SerializerMethodField递归序列化子节点,并仅在存在子节点时显示children字段:

# Serializers.py
from rest_framework import serializers
from .models import HierarchyData

class HeirarchyDataSerializer(serializers.ModelSerializer):
    children = serializers.SerializerMethodField(required=False)

    class Meta:
        model = HierarchyData
        fields = ["id", "region"]

    def get_children(self, obj):
        # 仅当存在子节点时递归序列化
        if obj.children.exists():
            return HeirarchyDataSerializer(obj.children.all(), many=True, context=self.context).data
        return None

    def to_representation(self, instance):
        # 移除空的children字段,匹配目标输出格式
        data = super().to_representation(instance)
        children_data = self.get_children(instance)
        if children_data:
            data['children'] = children_data
        return data

3. 保持视图代码不变

无需修改现有ListHierarchyData视图,因为自定义Manager已经让objects.all()返回过滤后的根节点并预取了所有子节点,序列化器会自动处理嵌套结构。

效果验证

调用/region端点后,会返回目标格式的完整嵌套树形结构,且不会出现超时或序列化错误,同时支持任意深度的层级扩展。

内容的提问来源于stack exchange,提问作者Sher Khan Mari

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 21:15:47