You need to enable JavaScript to run this app. Django REST Framework 递归嵌套模型的接口实现与分页方案
优惠活动
大模型
产品
解决方案
定价
更多

Django REST Framework 递归嵌套模型的接口实现与分页方案

最简实现方案

你初步设计的端点结构完全匹配业务需求,无需大幅调整,仅需通过DRF原生能力补充混合序列化、查询合并、分页逻辑即可,全程不需要引入第三方复杂依赖。

最终落地路由

直接复用你规划的路径结构:

  • lockers/<int:locker_id>/metadata/:返回指定Locker根层级(所有内容parent为None)下的混合文件、文件夹列表
  • lockers/<int:locker_id>/folders/<int:pk>/metadata/:返回指定文件夹内部嵌套的混合文件、文件夹列表
  • lockers/<int:locker_id>/uploads/:文件上传创建接口
  • lockers/<int:locker_id>/folders/:文件夹的创建、独立列表接口

核心逻辑实现

1. 统一结构的独立序列化器

为两类模型编写结构对齐的独立序列化器,添加统一类型标识字段供前端做交互区分,不需要复杂的多态序列化继承:

class FolderSerializer(serializers.ModelSerializer):
    item_type = serializers.SerializerMethodField()

    class Meta:
        model = Folder
        fields = ["id", "name", "parent", "item_type", "created_time", "modified_time"]

    def get_item_type(self, obj):
        return "folder"


class UploadSerializer(serializers.ModelSerializer):
    item_type = serializers.SerializerMethodField()

    class Meta:
        model = Upload
        fields = ["id", "name", "parent", "item_type", "file", "created_time", "modified_time"]

    def get_item_type(self, obj):
        return "upload"

统一的item_type字段让前端拿到列表后可以直接判断条目类型,不需要额外发起请求做二次识别。

2. 合并查询逻辑

两类模型的查询条件完全对称,只需要根据请求的层级范围(根目录/指定文件夹)拉取对应数据,再合并为统一列表:

  • 根目录场景查询规则:
    • 匹配条件1:Folder表中locker=目标对象、parent=None的所有记录
    • 匹配条件2:Upload表中locker=目标对象、parent=None的所有记录
  • 指定文件夹场景查询规则:
    • 匹配条件1:Folder表中locker=目标对象、parent=当前文件夹对象的所有记录
    • 匹配条件2:Upload表中locker=目标对象、parent=当前文件夹对象的所有记录

注意:不要直接对两个不同模型的QuerySet做union操作,因为两类模型字段不完全一致会触发报错,正确的轻量实现是将两个查询集序列化后按统一排序规则合并即可。

3. 复用式视图与分页实现

两个元数据端点的逻辑完全一致,可以抽成一个公共视图,路由层做参数透传,避免重复代码:

from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.views import APIView
from django.shortcuts import get_object_or_404

# 自定义分页规则,复用DRF原生分页能力
class MetadataPagination(PageNumberPagination):
    page_size = 20
    page_size_query_param = "page_size"
    max_page_size = 100


class LockerMetadataView(APIView):
    pagination_class = MetadataPagination

    def get(self, request, locker_id, folder_pk=None):
        # 先做权限校验,确保用户只能访问自己的Locker
        locker = get_object_or_404(Locker, pk=locker_id, owner=request.user)
        # 校验目标文件夹的归属,避免越权
        target_folder = None
        if folder_pk:
            target_folder = get_object_or_404(Folder, pk=folder_pk, locker=locker)

        # 拉取两类条目
        folders = Folder.objects.filter(locker=locker, parent=target_folder)
        uploads = Upload.objects.filter(locker=locker, parent=target_folder)

        # 合并序列化结果
        combined_entries = []
        combined_entries += FolderSerializer(folders, many=True).data
        combined_entries += UploadSerializer(uploads, many=True).data

        # 统一按创建时间倒序排序,和文件管理器的展示逻辑对齐
        combined_entries.sort(key=lambda x: x["created_time"], reverse=True)

        # 对合并后的全量列表做分页,不是分开对两类条目单独分页
        page = self.paginate_queryset(combined_entries)
        if page is not None:
            return self.get_paginated_response(page)
        return Response(combined_entries)

路由层直接把两个元数据路径绑定到同一个视图,不需要写两套重复逻辑:

urlpatterns = [
    # Locker根目录元数据列表
    path("lockers/<int:locker_id>/metadata/", LockerMetadataView.as_view()),
    # 指定文件夹下的元数据列表
    path("lockers/<int:locker_id>/folders/<int:folder_pk>/metadata/", LockerMetadataView.as_view()),
    # 上传、文件夹的独立CRUD接口走常规ModelViewSet实现即可,无需特殊处理
]

实现优势

  • 全程基于DRF原生能力,没有额外学习成本和依赖
  • 分页作用在合并后的总列表上,返回的单页结果是按规则排序的混合条目,和前端文件管理器的交互体验完全对齐
  • 两个元数据端点逻辑完全复用,后续调整列表返回字段只需要修改序列化器一处即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.25 13:24:02