跳转至

菜单系统

如果你熟悉 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:排序权重,数值越小越靠前,默认 10000
  • icon:图标名称,使用 Tabler Icons 或自定义图标库中的图标,如 tabler-book
  • remark:备注,仅开发者可见,不显示在前端

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

父级菜单项的 keystr 类型,默认为 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 后须重新执行同步,同步会自动清除缓存。若菜单显示未更新,请确认同步是否已执行。