当前位置: 首页 > news >正文

【Dv3Admin】系统视图菜单管理API文件解析

后台菜单管理模块是权限控制系统的核心部分,决定了用户可以访问的功能范围和页面结构。通过菜单配置可动态生成前端路由,实现权限隔离与导航逻辑统一,成为前后端联动的基础。

内容围绕 dvadmin/system/views/menu.py 文件展开,分析菜单接口的增删改查实现、路由数据生成、批量操作与顺序调整方法,结合序列化器设计与权限绑定机制,解读模块在实际系统中的角色与重要性。

文章目录

  • menu.py
  • 项目源码解析
  • 应用案例
  • 总结

menu.py

本系统采用 Django 框架搭建后台管理平台,dvadmin/system/views/menu.py 文件属于系统权限管理模块。菜单模块负责定义系统功能入口结构,通过配置菜单层级关系与前端页面路由映射,实现前后端统一的权限体系管理。该模块支持菜单的增删改查操作,关联按钮权限、字段权限等子模块,确保权限系统的完整性与灵活性。

项目特点描述
技术栈Django + Django Rest Framework
功能定位后台菜单管理、路由生成、权限控制入口管理
设计原则菜单、按钮、字段三级权限体系,灵活配置功能访问
配置方式后台界面动态配置菜单结构,实时生效

dvadmin/system/views/menu.py 文件提供菜单管理相关接口,通过继承自定义基础类 CustomModelViewSet 实现菜单记录的查询、创建、更新、删除操作。文件内部定义了 MenuSerializer 序列化器负责数据校验与格式转换,支持批量删除、菜单树形结构返回等扩展功能。该模块为权限系统提供了基础数据支持,配合角色管理模块可实现基于角色的菜单授权。

模块职责说明
定义菜单序列化器统一管理菜单数据的输入与输出格式
菜单列表接口支持分页查询、条件筛选,返回菜单基础信息
菜单树结构接口返回带层级关系的菜单树,用于前端动态生成路由导航
菜单增删改接口允许后台管理人员灵活新增、编辑或删除菜单项
支持批量操作实现批量删除菜单记录,提升后台管理效率

在构建权限控制平台、后台管理系统、内容管理系统等项目中,需要通过菜单模块定义功能入口,并与权限系统关联。通过 dvadmin/system/views/menu.py 提供的接口,可以动态配置系统菜单,控制不同角色的功能访问范围,实现前后端分离项目中的动态路由生成、权限拦截等核心功能。

使用场景说明
后台动态菜单管理后台界面可添加、编辑、删除菜单,实时生效
前端基于菜单生成路由前端应用通过菜单接口生成左侧导航栏及页面路由
角色授权菜单选择后台授权角色时,选择关联的菜单权限范围
多层级菜单支持支持二级、三级甚至更多层级的菜单结构
功能模块分组与展示优化通过菜单配置功能模块分组,提高系统可用性与易用性

项目源码解析

菜单数据标准序列化

MenuSerializer 负责把菜单表数据格式化输出,附加了菜单按钮权限集合 menuPermission 和子节点存在标记 hasChild。它与 Menu 模型和 MenuButton 模型的数据联动密切,增强了菜单管理的可扩展性,适用于后台管理和前端路由构建。

class MenuSerializer(CustomModelSerializer):menuPermission = serializers.SerializerMethodField(read_only=True)hasChild = serializers.SerializerMethodField()def get_menuPermission(self, instance):queryset = instance.menuPermission.order_by('-name').values('id', 'name', 'value')return queryset if queryset else Nonedef get_hasChild(self, instance):hasChild = Menu.objects.filter(parent=instance.id)return bool(hasChild)class Meta:model = Menufields = "__all__"read_only_fields = ["id"]

菜单创建处理器

MenuCreateSerializer 用于新增菜单时自动处理排序逻辑,避免手动输入排序值。它根据同一父节点下已有菜单的最大排序值进行递增,保持菜单顺序自然生长,提升了数据录入的便捷性和一致性。

class MenuCreateSerializer(CustomModelSerializer):name = serializers.CharField(required=False)def create(self, validated_data):menu_obj = Menu.objects.filter(parent_id=validated_data.get('parent', None)).order_by('-sort').first()last_sort = menu_obj.sort if menu_obj else 0validated_data['sort'] = last_sort + 1return super().create(validated_data)class Meta:model = Menufields = "__all__"read_only_fields = ["id"]

前端菜单路由序列化器

WebRouterSerializer 定义了前端所需路由数据格式,将菜单的地址、组件、可见性、缓存策略等关键信息打包。它通过字段映射简化前后端接口对接,提高了菜单动态渲染的效率和一致性。

class WebRouterSerializer(CustomModelSerializer):path = serializers.CharField(source="web_path")title = serializers.CharField(source="name")class Meta:model = Menufields = ('id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'link_url','is_catalog', 'web_path', 'component', 'component_name', 'cache', 'visible','is_iframe', 'is_affix', 'status')read_only_fields = ["id"]

菜单管理接口控制器

MenuViewSet 实现菜单资源的标准增删改查接口,同时扩展了多种自定义接口,包括前端路由生成、菜单懒加载查询、菜单移动排序等。与 Menu 模型、RoleMenuPermission 权限模型深度协作,是整个权限体系路由和展示层的重要组成模块。

class MenuViewSet(CustomModelViewSet):queryset = Menu.objects.all()serializer_class = MenuSerializercreate_serializer_class = MenuCreateSerializerupdate_serializer_class = MenuCreateSerializersearch_fields = ['name', 'status']filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'is_catalog']

list 方法根据请求参数动态过滤菜单数据,支持分页,也支持按父节点懒加载子菜单结构。默认只查询一级菜单,提升了前端加载速度和用户体验。

def list(self, request):request.query_params._mutable = Trueparams = request.query_paramsparent = params.get('parent', None)page = params.get('page', None)limit = params.get('limit', None)if page:del params['page']if limit:del params['limit']if params:queryset = self.queryset.filter(parent=parent) if parent else self.queryset.filter()else:queryset = self.queryset.filter(parent__isnull=True)queryset = self.filter_queryset(queryset)serializer = MenuSerializer(queryset, many=True, request=request)data = serializer.datareturn SuccessResponse(data=data)

web_router 动态生成当前用户可访问的前端路由数据,区分超级管理员和普通用户,按照角色绑定权限菜单生成菜单树结构。该接口保证前后端动态权限同步,提升系统灵活性。

@action(methods=['GET'], detail=False, permission_classes=[])
def web_router(self, request):user = request.userif user.is_superuser:queryset = self.queryset.filter(status=1).order_by("sort")else:role_list = user.role.values_list('id', flat=True)menu_list = RoleMenuPermission.objects.filter(role__in=role_list).values_list('menu_id', flat=True)queryset = Menu.objects.filter(id__in=menu_list).order_by("sort")serializer = WebRouterSerializer(queryset, many=True, request=request)data = serializer.datareturn SuccessResponse(data=data, total=len(data), msg="获取成功")

get_all_menu 用于后台菜单管理模块,返回系统内所有菜单数据。如果是超级管理员返回全部,否则根据用户角色返回权限内菜单,确保数据访问符合权限控制规则。

@action(methods=['GET'], detail=False, permission_classes=[])
def get_all_menu(self, request):user = request.userqueryset = self.queryset.all()if not user.is_superuser:role_list = user.role.values_list('id', flat=True)menu_list = RoleMenuPermission.objects.filter(role__in=role_list).values_list('menu_id')queryset = Menu.objects.filter(id__in=menu_list)serializer = WebRouterSerializer(queryset, many=True, request=request)data = serializer.datareturn SuccessResponse(data=data, total=len(data), msg="获取成功")

move_up 用于调整菜单的顺序,将当前菜单节点与同一父节点下排序更小的节点互换位置,实现菜单结构的手动调整。该接口保证了菜单顺序的灵活调整,便于前端结构优化。

@action(methods=['POST'], detail=False, permission_classes=[])
def move_up(self, request):menu_id = request.data.get('menu_id')try:menu = Menu.objects.get(id=menu_id)except Menu.DoesNotExist:return ErrorResponse(msg="菜单不存在")previous_menu = Menu.objects.filter(sort__lt=menu.sort, parent=menu.parent).order_by('-sort').first()if previous_menu:previous_menu.sort, menu.sort = menu.sort, previous_menu.sortprevious_menu.save()menu.save()return SuccessResponse(data=[], msg="上移成功")

move_down 用于将当前菜单节点与同一父节点下排序更大的节点互换位置,实现向下移动操作。配合 move_up 接口,用户可以灵活调整菜单展示顺序,优化系统管理体验。

@action(methods=['POST'], detail=False, permission_classes=[])
def move_down(self, request):menu_id = request.data['menu_id']try:menu = Menu.objects.get(id=menu_id)except Menu.DoesNotExist:return ErrorResponse(msg="菜单不存在")next_menu = Menu.objects.filter(sort__gt=menu.sort, parent=menu.parent).order_by('sort').first()if next_menu:next_menu.sort, menu.sort = menu.sort, next_menu.sortnext_menu.save()menu.save()return SuccessResponse(data=[], msg="下移成功")

应用案例

动态菜单系统在权限与路由控制中的集成实践

在后台管理系统中,菜单结构不仅承担页面导航职责,更是用户权限体系的核心入口。项目通过 dvadmin/system/views/menu.py 模块构建了完整的菜单资源管理体系,支持菜单的层级构建、路由信息生成、权限角色绑定、排序调整与前端懒加载展示,解决了动态配置页面结构与权限控制统一的问题。

功能点内容描述
场景需求菜单结构既需满足页面导航需求,又需作为用户权限体系的核心入口,实现动态配置页面结构与权限控制的统一。
核心模块dvadmin/system/views/menu.py:实现菜单资源管理体系。
支持功能- 层级构建:支持多层级菜单的创建,满足复杂页面导航需求。
- 路由信息生成:为前端动态生成页面路由信息,实现无缝对接。
- 权限角色绑定:菜单与权限角色绑定,确保基于角色的访问控制。
- 排序调整:支持按需调整菜单显示顺序,优化用户体验。
- 前端懒加载展示:按需加载菜单数据,提升页面加载性能与效率。
应用场景- 动态导航:根据用户角色动态展示对应的菜单项。
- 权限控制:确保用户仅能访问被授权的页面与功能。
优势- 统一管理菜单与权限,降低维护成本。
- 动态适配多角色、多权限的复杂场景需求。
扩展能力- 可与多种前端框架(如 Vue、React)集成,动态渲染菜单结构。
- 支持国际化菜单名称与动态多语言切换。

管理员可在后台添加菜单项,例如:

POST /api/system/menu/{"name": "用户管理","web_path": "/user","component": "system/user/index","parent": null,"is_catalog": false
}

该操作调用 MenuCreateSerializer 自动生成排序字段,系统根据当前菜单父节点下已有菜单项的最大排序值自动加一,保证菜单按创建顺序有序排列。前端可根据此数据动态生成页面路由和导航结构。

用户登录系统后,前端通过以下接口获取该用户可访问的菜单路由:

GET /api/system/menu/web_router/

调用 MenuViewSet.web_router() 方法,系统判断当前用户是否为超级管理员,若是则返回所有状态为启用的菜单项,否则根据其绑定角色返回授权内菜单数据,并返回如下格式:

[{"id": 1,"path": "/user","component": "system/user/index","title": "用户管理","visible": true,"cache": true,"is_link": false}
]

前端通过该数据生成左侧导航菜单与路由配置,实现权限内菜单的动态加载与展示。

管理员也可以调整菜单顺序,比如将“角色管理”菜单上移,通过接口:

POST /api/system/menu/move_up/{"menu_id": 8
}

系统会查找该菜单在同级菜单中的前一个排序项,并交换二者 sort 字段,完成排序更新。调用 move_down 接口则可实现向下移动操作,整个过程无需刷新页面,调整立即生效。

系统还提供 get_all_menu 接口用于后台授权页面加载所有菜单项,支持基于角色授权功能权限,前端通过该接口加载完整菜单树:

GET /api/system/menu/get_all_menu/

返回值依据当前用户权限做过滤,确保普通用户仅能看到自己有权访问的菜单项。整个菜单管理模块支撑了页面导航配置、权限粒度控制、菜单分组展示等功能,是权限系统与前端路由联动的基础。

总结

模块通过标准视图集封装菜单管理接口,结合定制序列化器完成数据校验与输出。新增与排序逻辑联动,简化后台录入流程。菜单树接口和动态路由接口适配不同前端场景,保证了权限系统与导航结构的一致性。整体代码结构清晰,具备良好的扩展性与维护性。

接口中过多依赖 ORM 查询与逻辑处理混合,缺乏查询优化。菜单树接口在节点多时可能产生性能瓶颈。排序调整通过交换字段完成,未加事务控制,存在数据竞争风险。如重构,可引入异步查询优化树结构生成,细化接口权限分离,提升稳定性与性能。

http://www.xdnf.cn/news/953209.html

相关文章:

  • ArcGIS Pro 3.4 二次开发 - 栅格
  • 【李沐-动手学深度学习v2】1.Colab学习环境配置
  • 如何给浏览器安装WeTab插件
  • 安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
  • 图片组件baseImage
  • Redux完整指南:从入门到精通
  • 群创3.5寸液晶模组LQ035NC111参数资料
  • PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础
  • 冯诺依曼架构是什么?
  • 在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
  • 前端与服务器交互以及前端项目组成。
  • 2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】
  • 【Proteus仿真】【32单片机-A011】HX711电子秤系统设计
  • BIO、NIO、AIO的区别
  • EtherCAT主站转Profinet网关接IS620N伺服驱动器与西门子plc通讯案例
  • Qt Http Server模块功能及架构
  • 【Java多线程从青铜到王者】单例设计模式(八)
  • markdown,nodejs前世今生以及内置模块相关知识点
  • AI原生应用实战:用户画像建模的7种机器学习方法
  • 力扣面试150题--蛇梯棋
  • 开发Vue.js组件的二三事
  • if 选择结构
  • 下载https协议的网络图片,并转为Base64
  • 浅谈非理想性因素对星座图的影响
  • ArcGIS Pro制作水平横向图例+多级标注
  • PIN码vs密码,电脑登录的快捷键你用对了吗?
  • CppCon 2015 学习:STL Algorithms in Action
  • Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
  • The Trade Desk推出DealDesk,试图让交易ID不再糟糕
  • HTTP 与 TCP 协议的区别与联系