# pear_admin_django **Repository Path**: One_PieceDC/pear_admin_django ## Basic Information - **Project Name**: pear_admin_django - **Description**: 基于Django + layui + ajax 的半分离的 rbac 后台管理系统 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 26 - **Forks**: 3 - **Created**: 2024-10-15 - **Last Updated**: 2025-07-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: Django, Ajax, layui ## README

Pear Admin Django

基于Django + layui 实现的半分离 rbac 后台管理系统

☆ 项目部署地址: `http://47.109.154.134/` 超级管理员账户: 15543526531 ; 密码: 123456 ## 项目简介 该项目,实现了一个基于Django + layui + ajax 的半分离的 rbac 后台管理系统. 在此基础上可以快速构建自己的运维管理系统 PS: 本项目的实现参考了开源项目 pear-admin-flask(main分支以及master分支)的业务逻辑. 开发该项目的心路历程(自己是如何一步步实现该项目的, 一些实现细节以及思考) 详见博客: `https://dchelloworld.cn/project/djangolayui/` ## 内置功能 - [x] 登陆模块、权限认证模块、动态菜单模块 - [x] 部门管理模块、权限管理、角色管理、用户管理 - [x] 操作日志 - 注 - 在中间件里 [在请求角度] 进行了权限拦截、在动态菜单里 [在用户操作的角度] 进行了权限拦截. - 注 - 实现了每条记录的软删除, 实现了用户、权限以及角色的禁用功能. - 请求的角度 用户的软删除和禁用在中间件的登陆认证部分是有体现的; 权限的软删除和禁用在 中间件的权限认证部分 以及登陆成功后的权限放入session 是有体现的; - 用户的角度 角色和权限的 软删除、禁用 在动态菜单加载的视图里是有体现的. - 注 - 权限粒度控制到按钮? 仅通过中间件里的权限认证判定了.. 在页面上就算没有删除权限, 删除按钮依旧还在, 只不过点击它, 会告诉你, 你没有该权限.. 敲黑板, 每个请求一来, 都会经过中间件.. ```python - 总结需求时,突然的一点思考: 看代码,至于角色的禁用和删除,在中间件的权限认证里没有判定. 意味着只有用户重新登陆后,角色的禁用才会作用到该用户上. 也可以理解成,登陆成功后 用户通过角色拿到权限 这一过程, 要在权限认证里再写一遍. 那登陆成功后,权限放session里就没必要了啊! > 其实为了减少数据库的查询,可以考虑 将 用户、角色、权限 禁用后 清除 该用户、角色、权限 对应用户的session [强迫让其重新登陆], 重新登陆获取新的权限存入session, 然后每次请求一来仅根据这个权限session来进行权限验证!! 我懒得改了,凑合着用吧,在前后端分离的vue+drf项目中,我再实现该思路!! ``` 各模块实现的需求以及业务逻辑, 如下: ### 登陆模块 ```python - 需求1: 用户输入用户名和密码, 登陆成功后, 自动跳转首页; 未登陆成功, 提示用户 用户名或密码错误. - 登陆成功后 ★ 用session来实现登陆会话的保持 - 登陆成功后 ★ 当前用户所拥有的权限用列表形式存储并放到Django的session表中 - 需求2: 用户访问某页面(eg:首页), 若未登陆自动跳转登陆页面. - 需求3: 当超级管理员将该用户的状态改为了禁用,应该立即生效,该用户访问任意页面都应该跳转登陆页面. 并且被禁用的用户在尝试登陆时,会告知该用户,此账号已被禁用. - 需求4: 当超级管理员将该用户删除(软删除)后, 与需求3同理. - 需求5: 登陆认证应有白名单 技术实现: form表单校验、session实现会话保持、中间件里实现登陆认证 ``` ### 权限认证 ```python - 需求1: 若当前登陆用户是超级管理员,无需权限认证 - 需求2: 用户若无权访问当前访问的页面或功能, 展示403权限认证失败页面,借此提示此账号没有该功能权限. - 需求3: 只要超级管理员在权限管理模块将某权限的状态改为了禁用或软删除了某权限 那么给该用户分配的角色有该权限也没用,权限认证依旧通过不了 - 需求4: 权限认证应有白名单 技术实现: 中间件里实现权限认证 ``` ### 动态菜单模块 ```python - 需求1: 若当前登陆用户是超级管理员,则左侧菜单默认会根据所有权限(除了软删除的)进行动态加载. - 需求2: 登陆成功后跳转的首页,首页会自动加载左侧的动态菜单.. - 左侧菜单会根据当前登陆用户的所拥有的权限进行动态加载!! - 若当前登陆用户所拥有的角色的状态被超级管理员改为了禁用,那么该角色对应的权限不应该出现在动态菜单中 - 若 用户-角色-权限, 某个权限的状态被超级管理员改为了禁用,那么该禁用的权限不应该出现在动态菜单中 - 若 用户-角色-权限, 软删除的角色和权限也应排除在外. ``` 提一嘴: 权限类型分为menu和meun-z和auth, auth是不可作菜单的. menu-菜单、menu-z节点、auth权限 > 详看权限管理的树结构就明白了 ### 部门管理 ```python [新增post] - 需求1: 新增的部门的上级部门id需有效 - <外键字段自身> - 需求2: 新增的部门名不能跟已存在的部门名重名 - <视图函数中 or clean中皆可> [编辑post] - 需求1: 编辑的部门的id需有对应的对象 - 需求2: 修改的上级部门id需有效 - <外键字段自身> - 需求3: 不能设置自己为父部门. <视图函数中,便于拿到edit_id 通过id来比!!不能通过name,编辑表单name改了,式子肯定不会成立的.> - 需求4: 修改的部门名不能跟除自己以外的其他已存在的部门名重名 - <视图函数中,便于拿到edit_id> [删除post] - 需求1: 删除的部门的id需有对应的对象 - 需求2: 有子部门是无法删除当前部门的 - 需求3: 部门软删除 ``` ### 权限管理 ★注意,新增权限后,需要给用户的角色赋予这个权限.. 不然刷新页面, 同步更新的用户的动态菜单中不会有相关的权限.. ```python [新增、编辑] - 获取权限的父权限下拉框,选择的pid只能是菜单或节点,不能是权限! - 关于菜单、节点、权限的单选框 > 前端进行单选框切换实现某些表单项的隐藏和展示 以及 特定选择下必填项的校验 > 后端根据表单的单选框的选择(项目中该单选框的name值叫kind) - 看pid是否必须 - 看哪些字段的值在存储时应该覆盖为空 [新增] - 权限名称不能跟已存在的权限名重名 [编辑] - 权限名称不能跟除自己以外的其他已存在的权限名重名 进行了软删除,又编写了新增不能重复名的问题.被软删除的权限也算到了里面,不算的话,加个判断即可!!我加了. - 在权限编辑时,同样的要实现"权限状态-禁用启用"的逻辑!! [删除] 同部门管理 0. 删除的权限的id需有对应的对象 1. 有子权限是无法删除当前权限的 2. 权限的软删除 [权限状态的逻辑] 关于权限的状态 - 禁用|启用 - 需求1: (操作者通过apifox发生请求)判断该权限是否已经不存在或已删除, 是的话, 提示操作者. - 需求2: 判断该权限的子权限在使用中, 若在使用, 则该权限不能禁用. - 需求3: 父权限和子权限原本都是处于禁用状态, 当启用一个子权限时, 其父权限同步自动启用.. - 需求4: 权限的禁用对超级管理员应不起作用 (权限认证是在中间件中的,若是superadmin,我直接返回的None. 意味着,只要是超级管理员登陆,所有路由的访问畅通无阻) Ps:若其他管理员拥有了"权限状态"这个权限 他将"获取权限"这个权限的权限状态改为了禁用,那么他自己在表格里就看不到权限树了!! - 若父权限没生效,那么该父权限下的所有子权限一并都不应该生效!! (在状态禁用的逻辑里变相实现了. ``` 细品下 权限树中 权限的三个类型 menu和meun-z和auth ```python """ 在clean方法中! 首先,kind字段自身就是不为空的.字段自身校验没错的话,kind肯定有值. 可以将项目左侧菜单理解成 -- 一级菜单`menu` -- 一级菜单`menu` -- 二级菜单`menu` -- 节点`menu-z` -- 三级菜单`menu` -- 节点`menu-z` -- 节点`menu-z` -- 权限`auth` -- 权限`auth` -- 节点`menu-z` > 规则怪谈: 新增的权限有三种类型-菜单menu、节点menu-z、权限auth。 菜单可以放到任意层级的菜单下,也可以不选则自己作为顶级菜单 ; 节点可以放到已有的任意层级的菜单下 ; 权限都必须放到节点下!! > 数据库中: (0代表不需要、1代表需要 图标 权限标识 访问路径 pid是否必选 "menu" 1 0 0 0 "menu-z" 0 1 1 1 "auth" 0 1 1 1 """ ``` ### 角色管理 我就粘贴复制一通, 改吧改吧就完成了角色的CURD和状态的更改. - 因为所涉及的知识点前面都用到啦!! ```python [新增] - 角色名称不能跟已存在的角色名重名 [编辑] - 编辑的角色的id需有对应的对象 - 角色名称不能跟除自己以外的其他已存在的角色名重名 [删除] 0. 删除的角色的id需有对应的对象 1. 角色的软删除 [状态更改] - 状态更改的角色的id需有对应的对象 [角色授权] - 点击角色主页的授权,加载角色授权的iframe页面(携带了角色id),该页面会自动发送请求获取权限树并自动勾选当前角色所拥有的权限. - ★ 关于当前角色所拥有的权限,因为layui tree组件的缘故,我们只需返回树中叶子节点的id!! - 点击角色授权页面上的重置按钮,重新进行发送请求获取权限树并自动勾选当前角色所拥有的权限. - 点击角色授权页面上的确认按钮,前端通过递归采集到勾选的id,后端进行多对多的更新!!(先清空再添加) ``` ### 用户管理 ps: 用户管理那的筛选功能, 点击重置后, 时间范围的选择好像没有重置成功, 有的小bug, 不想改了.. ```python [用户主页] > 分页和筛选条件都是构造?params参数,在请求用户表格数据时,携带该参数!! - 分页 - 筛选条件 名称、性别、职位、注册时间、是否启用 + 部门 > 因为是查询,后端直接查 部门树传递到后端的勾选id即可!! (不用考虑是否是叶子节点,非叶子节点是 经理或CEO.一并查询出来,无伤大雅 [用户 增加、修改、删除] 1.手机号、邮箱是唯一的 (昵称和用户名我们允许重名 2.密码和重复密码需一致 3.部门前端可不填,不填则表示待定. [用户授角] 详看代码! ``` ### 日志管理 ```python - 在rbac组件的app.py里注册信号!! 通过信号来记录 新增、编辑、删除的记录. - 在中间件里通过process_exception来捕获了请求相关的异常并记录到日志表中!! ``` ## 项目预览 | | | | ------------------------------------------------------------ | ------------------------------------------------------------ | | ![image-20241225103519784](./assets/image-20241225103519784.png) | ![image-20241225103611831](./assets/image-20241225103611831.png) | | ![image-20241225103656062](./assets/image-20241225103656062.png) | ![image-20241225103919937](./assets/image-20241225103919937.png) | | ![image-20241225104014729](./assets/image-20241225104014729.png) | ![image-20241225104056429](./assets/image-20241225104056429.png) | | ![image-20241225104744854](./assets/image-20241225104744854.png) | ![image-20241225105021485](./assets/image-20241225105021485.png) | | ![image-20241225104936262](./assets/image-20241225104936262.png) | ![image-20241225105652358](./assets/image-20241225105652358.png) | ## 项目结构 ```python pear_admin_django ├── LICENSE ├── README.md ├── apps │   ├── base # ORM数据表相关的应用 │   │   ├── __init__.py │   │   ├── apps.py │   │   ├── management │   │   │   ├── __init__.py │   │   │   └── commands # 自定义的manage.py命令,运行脚本用于批量添加数据 │   │   ├── migrations # 里面是数据库迁移记录文件 │   │   └── models │   │   ├── __init__.py # 原本写在一个py文件的多张表,我通过包的形式进行了拆分 │   │   ├── _base.py │   │   ├── department.py # 部门表 │   │   ├── log.py # 日志表 │   │   ├── rights.py # 权限表 │   │   ├── role.py # 角色表 │   │   └── user.py # 用户表 │   └── rbac # rbac用户-角色-权限相关的应用 │   ├── __init__.py │   ├── apps.py │   ├── signals.py # 信号,用于记录日志 │   ├── urls.py │   └── views │   ├── account # 登陆、登出、获取动态菜单 │   ├── department # 获取部门树、部门的CURD │   ├── log # 获取日志列表数据、删除日志 │   ├── rights # 下拉获取权限树、权限的CURD、权限状态的更改 │   ├── role # 角色的CURD、角色状态的更改、获取授权树、角色授权 │   ├── static_html # 里面是各个模块html页面的路由相关视图!!html页面的请求都汇总到这,其余的api全是ajax请求. │   └── user # 用户的curd、用户状态的更改、用户授角、上传用户头像、重置用户密码等 ├── assets # 这些截图是readme.md里项目预览用到的! │   ├── image-20241225103519784.png │   ├── image-20241225103611831.png │   └── image-20241225105652358.png ├── manage.py ├── media # 媒体文件,就是用户上传的用户头像!! │   └── upload │   ├── 1.jpeg │   ├── 1.png │   ├── 4.jpeg │   └── 4.png ├── package.json ├── pear_admin_django │   ├── __init__.py │   ├── asgi.py │   ├── local_settings.py # 本地配置文件!! │   ├── settings.py │   ├── urls.py │   └── wsgi.py ├── poetry.lock # poetry管理项目依赖 ├── pyproject.toml # poetry管理项目依赖 ├── requirements.txt # 可通过poetry来生成 ├── service │   ├── encrypt.py # md5加密 │   ├── middleware.py # 中间件 │   ├── no_use # 项目编写过程中的一些尝试,不用管它 │   ├── per_verify.py # 当前登陆用户的权限存入session中 │   ├── res_json_data.py # 规范每个api的返回格式!! │   └── ret.py # 规范code返回码 ├── shell # 项目部署到线上服务器时用的!! │   ├── reboot.sh │   ├── stop.sh │   └── uwsgi_pear.ini ├── static # 有些静态文件是直接拷贝的pear_admin_layui前端模版里的,有些时候自己写的!! │   ├── admin │   ├── avatar │   │   └── default.jpg # 用户的默认头像 │   ├── component │   │   ├── layui │   │   └── pear │   ├── config │   │   ├── pear.config.json # !!这个文件是可以根据需求自己更改的!! │   │   └── pear.config.yml │   ├── dc.js │   └── init_data # 批量添加数据的脚本!! │   ├── ums_department.csv │   ├── ums_rights.csv │   ├── ums_role.csv │   └── ums_user.csv └── templates    ├── error # 权限不足或出现异常时返回的页面    │   ├── 403.html    │   ├── 404.html    │   └── 500.html    ├── index.html # 这个文件的js部分,可看一下,跟前面的pear.config.json相呼应!    ├── login.html    ├── rbac # rbac组件的一些html模版    │   ├── common # 公共的模版内容,以便复用    │   ├── department    │   ├── log    │   ├── rights    │   ├── role    │   └── user    └── view # 额,这个是pear_admin_layui前端模版自带的一些页面,我只使用了里面的一两个页面.    ├── analysis    ├── component    ├── console    ├── exception    ├── listing    ├── profile    └── result ``` ## 运行项目 本项目环境及其依赖说明: `python3.9` 、`django3.2`、`mysql8.0`、`poetry`、`layui2.9.10`、`pear4.0.3` 以及layui第三方组件: `combotree`、`iconPicker` - step1: 克隆项目到本地 ```python git clone https://gitee.com/One_PieceDC/pear_admin_django.git ``` - step2: 安装虚拟环境. 在pycharm中打开该项目,通过pycharm创建虚拟环境,并进入该虚拟环境 - step3: 安装项目依赖 ```python pip install -r requirements.txt ``` - step4: 配置local_settings.py ```python DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'pear_admin_django', # -- 库名 'USER': 'root', 'PASSWORD': 'admin1234', 'HOST': '127.0.0.1', 'PORT': '3306', 'CHARSET': 'utf8' } } ``` - step5: 进行数据库迁移 ```python python manage.py makemigrations python manage.py migrate > 有时候没有成功,就执行 python manage.py makemigrations base python manage.py migrate base ``` - step6: 批量添加测试数据 ```python (.venv) MacBook-Air:pear_admin_django dengchuan$ python manage.py init_script 开始批量添加数据... Good,爱卿退下吧! Command executed successfully! ``` - step7: 使用命令运行项目 ```python (.venv) MacBook-Air:pear_admin_django dengchuan$ python manage.py runserver Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). December 25, 2024 - 12:41:38 Django version 3.2, using settings 'pear_admin_django.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. ``` ## 其他说明 - 项目部署: 详见: `https://dchelloworld.cn/pages/409rah/` - 关于我开发这个项目时的一些思考和心路历程, 可参考链接 `https://dchelloworld.cn/project/djangolayui/` - To do: 验证码登陆、微信登陆、在添加权限时,判断权限标识和url的添加在系统中是否存在(感觉没必要).. ---