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

如何在Django中构建带REST API的多级动态菜单?

多级动态菜单的Model与Serializer设计方案

一、Model优化设计

你用自关联ForeignKey实现树形菜单的思路完全正确,这是处理层级结构数据的经典方案。下面补充几个实用字段,让菜单功能更完善:

from django.db import models

class MenuPoint(models.Model):
    name = models.CharField(max_length=200, verbose_name="菜单名称")
    url = models.CharField(max_length=200, blank=True, null=True, verbose_name="跳转链接")
    icon = models.CharField(max_length=100, blank=True, null=True, verbose_name="菜单图标类名")
    order = models.IntegerField(default=0, verbose_name="排序权重")  # 控制同级菜单展示顺序
    is_active = models.BooleanField(default=True, verbose_name="是否启用")
    parent_menu_point = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        related_name='submenus',  # 改成更语义化的名称,后续序列化更直观
        blank=True,
        null=True,
        verbose_name="父级菜单"
    )

    class Meta:
        verbose_name = "菜单项"
        verbose_name_plural = "菜单项"
        ordering = ['order']  # 默认按排序权重升序排列

    def __str__(self):
        return self.name

补充字段说明:

  • url:存储菜单跳转的路由或外部链接,前端渲染时直接调用
  • icon:存储图标类名(比如el-icon-menu),方便前端展示菜单图标
  • order:解决同级菜单的排序问题,数值越小展示越靠前
  • is_active:无需删除数据即可控制菜单是否在前端显示
  • 调整related_name为submenus,比原命名更清晰,符合语义

二、Serializer设计(递归嵌套实现多级菜单)

要输出层级化的菜单结构,需要使用递归Serializer,Django REST Framework原生支持这种写法:

from rest_framework import serializers
from .models import MenuPoint

class MenuPointSerializer(serializers.ModelSerializer):
    # 递归引用自身,序列化子菜单
    submenus = serializers.SerializerMethodField()

    class Meta:
        model = MenuPoint
        fields = ['id', 'name', 'url', 'icon', 'order', 'is_active', 'submenus']

    def get_submenus(self, obj):
        # 仅序列化启用状态的子菜单,并按排序权重排序
        active_submenus = obj.submenus.filter(is_active=True).order_by('order')
        return MenuPointSerializer(active_submenus, many=True, context=self.context).data

视图层处理(返回顶级菜单)

在视图中只需查询父级为null的顶级菜单,Serializer会自动递归嵌套所有子菜单:

from rest_framework.views import APIView
from rest_framework.response import Response
from django.db.models import Prefetch
from .models import MenuPoint
from .serializers import MenuPointSerializer

class MenuListAPIView(APIView):
    def get(self, request):
        # 预加载子菜单,减少数据库查询次数(优化性能)
        top_level_menus = MenuPoint.objects.filter(
            parent_menu_point__isnull=True,
            is_active=True
        ).order_by('order').prefetch_related(
            Prefetch('submenus', queryset=MenuPoint.objects.filter(is_active=True).order_by('order'))
        )
        serializer = MenuPointSerializer(top_level_menus, many=True)
        return Response(serializer.data)

三、返回效果示例

接口返回的JSON结构会完全匹配你需要的多级菜单格式:

[
    {
        "id": 1,
        "name": "Menu 1",
        "url": "/menu1",
        "icon": "el-icon-menu",
        "order": 0,
        "is_active": true,
        "submenus": [
            {
                "id": 2,
                "name": "Submenu 1.1",
                "url": "/menu1/sub1",
                "icon": "el-icon-s-menu",
                "order": 0,
                "is_active": true,
                "submenus": [
                    {
                        "id": 3,
                        "name": "Submenu 1.1.1",
                        "url": "/menu1/sub1/sub1",
                        "icon": "",
                        "order": 0,
                        "is_active": true,
                        "submenus": []
                    },
                    {
                        "id": 4,
                        "name": "Submenu 1.1.2",
                        "url": "/menu1/sub1/sub2",
                        "icon": "",
                        "order": 1,
                        "is_active": true,
                        "submenus": []
                    }
                ]
            },
            {
                "id": 5,
                "name": "Submenu 1.2",
                "url": "/menu1/sub2",
                "icon": "el-icon-s-menu",
                "order": 1,
                "is_active": true,
                "submenus": []
            }
        ]
    },
    {
        "id": 6,
        "name": "Menu 2",
        "url": "/menu2",
        "icon": "el-icon-menu",
        "order": 1,
        "is_active": true,
        "submenus": []
    }
]

四、注意事项

  1. 如果菜单层级极深(超过10层),递归序列化可能引发性能问题,建议用prefetch_related预加载所有子菜单,减少数据库查询次数
  2. 若需结合权限控制,可在get_submenus方法中根据当前用户权限过滤子菜单
  3. 如果需要批量更新菜单排序,可考虑引入django-mptt库优化树形结构的操作效率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 01:35:55