菜单系统
如果你熟悉 Pinmok 的菜单定义方式,可以展开下方的快速开始,无需阅读正文。
快速开始
1. 在应用目录下新建 menus.py
2. 定义菜单
# 引入菜单定义函数
from pinmok.core import menu
# 定义菜单 变量 admin_menu 是约定的固定写法,不能随意写
admin_menu = [
menu('blog', title='Blog', icon='tabler-book', sort_order=0), # 根菜单项,无 parent_key
menu('posts', title='Posts', url='posts', parent_key='blog', sort_order=100), # 子菜单项,parent_key 指向 blog
menu('category', title='Category', url='category', parent_key='blog', sort_order=200),
]
key:第一个参数,菜单项唯一标识,同一个应用内不可重复title:菜单标题,显示在后台导航中。系统会自动调用多语言翻译,无需在定义时使用gettext处理,只需维护对应的翻译语言包即可url:页面地址,支持 URL name 或以 / 开头的绝对路径,根菜单可不填parent_key:父级菜单的 key,不填则为根菜单项sort_order:排序权重,数值越小越靠前,默认 10000icon:图标名称,使用 Tabler Icons 或自定义图标库中的图标,如 tabler-bookremark:备注,仅开发者可见,不显示在前端
3. 登录后台,点击左侧菜单栏顶部红色 ☰ 图标,确认同步
刷新页面后菜单即可生效。
概述
Django Admin 的菜单完全由已注册的模型驱动,没有对应模型便无法生成菜单入口,非数据操作类的业务功能也因此难以集成到后台。Pinmok 对此进行了扩展,允许开发者在
menus.py 中自由定义指向任意 URL 的菜单项,使得纯逻辑业务、工具页面、统计视图等非模型功能同样能够出现在后台菜单中。开发者既可以构建完全不依赖模型的独立应用,也可以将模型管理与自定义业务视图混合组织在同一套菜单结构下。
定义菜单
文件位置
在应用根目录下新建 menus.py 文件。同步菜单时,系统会自动扫描每个已安装应用根目录下的 menus.py,读取其中的菜单定义。注意,menus.py
必须位于应用根目录,放在子目录或子包中将无法被识别。文件名为系统约定,不可更改。
基本结构
menus.py 的结构很简单:从 pinmok.core 引入 menu() 函数,将所有菜单项定义为列表,赋值给约定变量名 admin_menu。系统同步时会从该变量中读取菜单定义,变量名不可更改。
from pinmok.core import menu
admin_menu = [
menu('blog', title='Blog Management', icon='tabler-book', sort_order=0),
menu('posts', title='Posts', url='posts', parent_key='blog', sort_order=100),
menu('category', title='Category', url='category', parent_key='blog', sort_order=200),
menu(
'stat',
title='Statistics',
url='post_stat',
parent_key='blog',
sort_order=300,
remark='Relevant data statistics of the posts.',
),
menu('dashboard', title='Data Dashboard', url='post_dashboard', parent_key='stat', sort_order=100),
menu('traffic', title='Traffic Analysis', url='post_traffic', parent_key='stat', sort_order=200),
]
以上定义在后台生成的菜单结构为:
Blog Management
├─ Posts
├─ Category
└─ Statistics
├── Data Dashboard
└── Traffic Analysis
一个应用可以定义多个根菜单项,不必把所有菜单项都归在同一个根节点下。
参数说明
menu() 函数的第一个参数 key 为位置参数,其余均为关键字参数:
key(必填)
菜单项的唯一标识符,str 类型。同一应用内不可重复,用于建立父子关系(parent_key)以及系统内部索引。
title(必填)
菜单标题,str 类型,显示在后台菜单上。定义时直接写原始字符串,不要用 gettext 处理。系统在渲染时会自动处理多语言翻译,
只需在翻译语言包中维护对应的翻译条目即可,无需在定义时做任何额外处理。
url
菜单项指向的页面地址,str 类型,默认为 None。支持两种写法:
- 以
/开头,视为绝对路径,直接使用 - 否则视为 URL name,系统在同步时自动调用
reverse()解析为实际路径
若 URL name 解析失败,同步操作将立即报错。没有子菜单项的根菜单通常不需要设置 url。
parent_key
父级菜单项的 key,str 类型,默认为 None。不设置则作为根菜单项。
sort_order
排序权重,int 类型,默认值为 10000。同级菜单按该值升序排列,数值越小越靠前。
icon
菜单图标,str 类型,默认为 None。填写后台图标管理中已有的图标名称,系统会从 sprite 文件或自定义图标中匹配渲染。
permissions
权限列表,list[str] 类型,默认为 None。格式为 Django 标准权限字符串,如 ["app.view_model"]。
注意:
permissions参数在当前版本中已可定义,但权限过滤功能尚未完整实现,该参数暂不生效。完整的权限控制支持将在后续版本中提供。
remark
备注信息,str 类型,默认为 None。不显示在前端,仅供开发者记录说明。
层级限制
菜单定义本身不限层级,但后台界面最多只支持三级显示,超过三级的菜单项将被丢弃,请在定义时注意控制层级深度。
同步菜单
菜单定义完成后,需要手动执行同步操作,将菜单数据写入数据库,后台才能正常显示。
如何同步
使用超级用户账号登录后台,在左侧菜单栏顶部有一个红色的 ☰ 图标,该入口仅超级用户可见。点击后会弹出确认提示——同步操作会清除当前所有菜单数据并重新写入,此操作不可逆。确认后刷新页面,新菜单即可生效。
何时需要重新同步
凡是菜单定义发生变更,均需重新同步,例如:
- 安装了新应用
- 修改了
menus.py中的菜单定义
同步操作会自动清除菜单缓存,无需手动处理。
与模型菜单的关系
Pinmok 在渲染菜单时,会自动将 menus.py 定义的菜单与 Django Admin 的模型注册菜单(app_list)合并。同一 app_label
下,两者的菜单项会合并在同一根节点下,而不是并列为两个独立的根菜单。
如果一个应用定义了多个根菜单项,模型菜单会被合并到列表中第一个根菜单项下。当前版本暂不支持指定合并目标,如有需要,调整 admin_menu 列表中根菜单项的定义顺序即可。
合并后的菜单统一按 sort_order 排序。对于模型菜单项,Pinmok 在 ModelAdmin 上扩展了 menu_sort_order 属性,用于控制模型菜单项在合并后的排序位置,默认值为
10000:
@padmin.register(EmailConfig)
class EmailConfigAdmin(ConfigModelAdmin):
menu_sort_order = 2000
如果后台存在混合菜单,请注意合理设置各菜单项的排序值,避免顺序混乱。
菜单缓存
Pinmok 对菜单数据实施两层缓存,以减少数据库查询开销。
第一层:数据库菜单缓存
从数据库加载的菜单数据整体缓存,有效期 1 小时。缓存键带有版本号,每次同步后版本号自动递增,旧缓存立即失效。
第二层:用户菜单缓存
经 app_list 合并后的最终菜单树按用户维度缓存,同样基于版本号失效机制,同步后自动清除。
开发期注意: 修改
menus.py后须重新执行同步,同步会自动清除缓存。若菜单显示未更新,请确认同步是否已执行。