后台菜单管理模块是权限控制系统的核心部分,决定了用户可以访问的功能范围和页面结构。通过菜单配置可动态生成前端路由,实现权限隔离与导航逻辑统一,成为前后端联动的基础。
内容围绕 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
模型的数据联动密切,增强了菜单管理的可扩展性,适用于后台管理和前端路由构建。
python
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 None
def get_hasChild(self, instance):
hasChild = Menu.objects.filter(parent=instance.id)
return bool(hasChild)
class Meta:
model = Menu
fields = "__all__"
read_only_fields = ["id"]
菜单创建处理器
MenuCreateSerializer
用于新增菜单时自动处理排序逻辑,避免手动输入排序值。它根据同一父节点下已有菜单的最大排序值进行递增,保持菜单顺序自然生长,提升了数据录入的便捷性和一致性。
python
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 0
validated_data['sort'] = last_sort + 1
return super().create(validated_data)
class Meta:
model = Menu
fields = "__all__"
read_only_fields = ["id"]
前端菜单路由序列化器
WebRouterSerializer
定义了前端所需路由数据格式,将菜单的地址、组件、可见性、缓存策略等关键信息打包。它通过字段映射简化前后端接口对接,提高了菜单动态渲染的效率和一致性。
python
class WebRouterSerializer(CustomModelSerializer):
path = serializers.CharField(source="web_path")
title = serializers.CharField(source="name")
class Meta:
model = Menu
fields = (
'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
权限模型深度协作,是整个权限体系路由和展示层的重要组成模块。
python
class MenuViewSet(CustomModelViewSet):
queryset = Menu.objects.all()
serializer_class = MenuSerializer
create_serializer_class = MenuCreateSerializer
update_serializer_class = MenuCreateSerializer
search_fields = ['name', 'status']
filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'is_catalog']
list
方法根据请求参数动态过滤菜单数据,支持分页,也支持按父节点懒加载子菜单结构。默认只查询一级菜单,提升了前端加载速度和用户体验。
python
def list(self, request):
request.query_params._mutable = True
params = request.query_params
parent = 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.data
return SuccessResponse(data=data)
web_router
动态生成当前用户可访问的前端路由数据,区分超级管理员和普通用户,按照角色绑定权限菜单生成菜单树结构。该接口保证前后端动态权限同步,提升系统灵活性。
python
@action(methods=['GET'], detail=False, permission_classes=[])
def web_router(self, request):
user = request.user
if 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.data
return SuccessResponse(data=data, total=len(data), msg="获取成功")
get_all_menu
用于后台菜单管理模块,返回系统内所有菜单数据。如果是超级管理员返回全部,否则根据用户角色返回权限内菜单,确保数据访问符合权限控制规则。
python
@action(methods=['GET'], detail=False, permission_classes=[])
def get_all_menu(self, request):
user = request.user
queryset = 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.data
return SuccessResponse(data=data, total=len(data), msg="获取成功")
move_up
用于调整菜单的顺序,将当前菜单节点与同一父节点下排序更小的节点互换位置,实现菜单结构的手动调整。该接口保证了菜单顺序的灵活调整,便于前端结构优化。
python
@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.sort
previous_menu.save()
menu.save()
return SuccessResponse(data=[], msg="上移成功")
move_down
用于将当前菜单节点与同一父节点下排序更大的节点互换位置,实现向下移动操作。配合 move_up
接口,用户可以灵活调整菜单展示顺序,优化系统管理体验。
python
@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.sort
next_menu.save()
menu.save()
return SuccessResponse(data=[], msg="下移成功")
应用案例
动态菜单系统在权限与路由控制中的集成实践
在后台管理系统中,菜单结构不仅承担页面导航职责,更是用户权限体系的核心入口。项目通过 dvadmin/system/views/menu.py
模块构建了完整的菜单资源管理体系,支持菜单的层级构建、路由信息生成、权限角色绑定、排序调整与前端懒加载展示,解决了动态配置页面结构与权限控制统一的问题。
功能点 | 内容描述 |
---|---|
场景需求 | 菜单结构既需满足页面导航需求,又需作为用户权限体系的核心入口,实现动态配置页面结构与权限控制的统一。 |
核心模块 | dvadmin/system/views/menu.py :实现菜单资源管理体系。 |
支持功能 | - 层级构建:支持多层级菜单的创建,满足复杂页面导航需求。 |
- 路由信息生成:为前端动态生成页面路由信息,实现无缝对接。 | |
- 权限角色绑定:菜单与权限角色绑定,确保基于角色的访问控制。 | |
- 排序调整:支持按需调整菜单显示顺序,优化用户体验。 | |
- 前端懒加载展示:按需加载菜单数据,提升页面加载性能与效率。 | |
应用场景 | - 动态导航:根据用户角色动态展示对应的菜单项。 - 权限控制:确保用户仅能访问被授权的页面与功能。 |
优势 | - 统一管理菜单与权限,降低维护成本。 - 动态适配多角色、多权限的复杂场景需求。 |
扩展能力 | - 可与多种前端框架(如 Vue、React)集成,动态渲染菜单结构。 - 支持国际化菜单名称与动态多语言切换。 |
管理员可在后台添加菜单项,例如:
http
POST /api/system/menu/
{
"name": "用户管理",
"web_path": "/user",
"component": "system/user/index",
"parent": null,
"is_catalog": false
}
该操作调用 MenuCreateSerializer
自动生成排序字段,系统根据当前菜单父节点下已有菜单项的最大排序值自动加一,保证菜单按创建顺序有序排列。前端可根据此数据动态生成页面路由和导航结构。
用户登录系统后,前端通过以下接口获取该用户可访问的菜单路由:
http
GET /api/system/menu/web_router/
调用 MenuViewSet.web_router()
方法,系统判断当前用户是否为超级管理员,若是则返回所有状态为启用的菜单项,否则根据其绑定角色返回授权内菜单数据,并返回如下格式:
json
[
{
"id": 1,
"path": "/user",
"component": "system/user/index",
"title": "用户管理",
"visible": true,
"cache": true,
"is_link": false
}
]
前端通过该数据生成左侧导航菜单与路由配置,实现权限内菜单的动态加载与展示。
管理员也可以调整菜单顺序,比如将"角色管理"菜单上移,通过接口:
http
POST /api/system/menu/move_up/
{
"menu_id": 8
}
系统会查找该菜单在同级菜单中的前一个排序项,并交换二者 sort
字段,完成排序更新。调用 move_down
接口则可实现向下移动操作,整个过程无需刷新页面,调整立即生效。
系统还提供 get_all_menu
接口用于后台授权页面加载所有菜单项,支持基于角色授权功能权限,前端通过该接口加载完整菜单树:
http
GET /api/system/menu/get_all_menu/
返回值依据当前用户权限做过滤,确保普通用户仅能看到自己有权访问的菜单项。整个菜单管理模块支撑了页面导航配置、权限粒度控制、菜单分组展示等功能,是权限系统与前端路由联动的基础。
总结
模块通过标准视图集封装菜单管理接口,结合定制序列化器完成数据校验与输出。新增与排序逻辑联动,简化后台录入流程。菜单树接口和动态路由接口适配不同前端场景,保证了权限系统与导航结构的一致性。整体代码结构清晰,具备良好的扩展性与维护性。
接口中过多依赖 ORM 查询与逻辑处理混合,缺乏查询优化。菜单树接口在节点多时可能产生性能瓶颈。排序调整通过交换字段完成,未加事务控制,存在数据竞争风险。如重构,可引入异步查询优化树结构生成,细化接口权限分离,提升稳定性与性能。