代码讲解
# 部门
- 若部门只有两层
res_data = []
first_queryset = Department.objects.filter(pid_id__isnull=True) # 拿到顶级部门
if first_queryset:
for item in first_queryset:
item_data = item.json()
res_data.append(item_data)
for child in item.department_set.all(): # 顶级部门:顶级部门所有子部门=1:n so,是反向查询
item_data['children'].append(child.json())
print(res_data)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 时序图
# 不分离
sequenceDiagram
participant U as 用户
participant B as 浏览器
participant S as 服务器
U ->> B: 点击部门菜单
B ->> S: GET /departments/
S ->> S: 查询db,将部门列表数据通过模版语法渲染到页面中
S -->> B: 返回部门列表页面
B ->> B: 渲染部门列表
alt 新增部门
U ->> B: 点击新增按钮
B ->> S: GET /departments/add/
S -->> B: 返回新增部门页面
B ->> B: 渲染新增部门表单
U ->> B: 填写表单并提交
B ->> S: POST /departments/add/
S -->> S: 校验新部门数据
alt 校验通过
S -->> S: 保存新部门数据
S -->> B: 返回部门列表页面
B ->> B: 跳转部门列表
else 校验不通过
S -->> B: 将错误信息渲染到表单上
B ->> B: 展示含有错误信息的表单
end
end
alt 修改部门
U ->> B: 点击编辑按钮
B ->> S: GET /departments/edit/{id}/
S -->> B: 返回编辑部门页面
B ->> B: 渲染编辑部门表单
U ->> B: 修改表单并提交
B ->> S: POST /departments/edit/{id}/
S -->> S: 校验更新的数据,同上,以校验通过为例
S -->> B: 返回部门列表页面
B ->> B: 跳转部门列表
end
alt 删除部门
U ->> B: 点击删除按钮
B ->> S: GET /departments/delete/{id}/ // 请求删除确认页面
S -->> B: 返回删除确认页面
B ->> B: 渲染删除确认页面
U ->> B: 确认删除
B ->> S: POST /departments/delete/{id}/
S -->> S: 校验删除的数据,同上,以校验通过为例
S -->> B: 返回部门列表页面
B ->> B: 跳转部门列表
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 半分离
sequenceDiagram
participant U as 用户
participant B as 浏览器
participant S as 服务器
U ->> B: 点击部门菜单
B ->> S: GET /rbac/department/main.html
S -->> B: 返回静态页面-部门主页
B ->> B: 渲染部门主页
U ->> B: 点击获取部门列表
B ->> S: GET /api/v1/department/treetable/
S -->> S: 查询部门列表,构建嵌套的数据结构
S -->> B: 返回JSON数据
B ->> B: 渲染部门列表到树形表格中
alt 新增数据
U ->> B: 点击新增按钮
B ->> S: GET /rbac/department/add.html
S -->> B: 返回静态页面-部门新增
B ->> B: 渲染部门新增页面到iframe窗口中
U ->> B: 点击新增提交按钮
B ->> S: AJAX POST /api/v1/department/add/
S -->> S: 新增数据处理
S -->> B: 返回JSON数据
B ->> B: 根据响应状态码和code字段处理响应
end
alt 修改数据
U ->> B: 点击修改按钮
B ->> S: GET /rbac/department/edit.html?id={id}
S -->> S: 通过模版语法,根据id将对象数据渲染到表单中,实现自动填充
S -->> B: 返回静态页面-部门修改
B ->> B: 渲染部门修改页面到iframe窗口中
U ->> B: 点击编辑提交按钮
B ->> S: AJAX POST /api/v1/department/edit/
S -->> S: 修改数据处理,要处理的对象的id在请求体中!
S -->> B: 返回JSON数据
B ->> B: 根据响应状态码和code字段处理响应
end
alt 删除数据
U ->> B: 点击删除按钮
B ->> S: AJAX POST /api/v1/department/del/
S -->> S: 删除数据处理,要处理的对象的id在请求体中!
S -->> B: 返回JSON数据
B ->> B: 根据响应状态码和code字段处理响应
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 动态菜单
pear.config.json文件中有几个地方需要注意下:
"menu": { "data": "/menu/", // - 菜单的数据接口 "method": "GET", "accordion": true, "collapse": false, "control": false, "controlWidth": 500, "select": "10", "select": "126", // - 默认选中的菜单的ID,需要去权限表里看下 "async": true }, "tab": { // - tab 标签导航栏,就主体内容上面那一栏,会展示你点击过哪些菜单项 "enable": true, "keepState": true, "session": true, "preload": false, "max": "30", "index": { // - 默认选中的菜单信息 "id": "126", "href": "view/analysis/index.html", // 去权限表里看下,首页的路径也要一样 "title": "首页" } },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23实现的动态菜单逻辑如下:
current_user = User.objects.first()
rights_obj_set = set()
for role_obj in current_user.role_list.all(): # 跨表拿到当前用户的所有角色
for rights_obj in role_obj.rights_list.all(): # 根据角色跨表拿到当前用户的所有权限
# 拿到全部权限,剔除掉type是auth的,剩下的就是可以做菜单的!
if rights_obj.type != "auth":
rights_obj_set.add(rights_obj)
# rights_obj_set是一大堆可作菜单的权限对象!! 根据对象的属性进行排序,先排pid,pid相同的排id. 倒叙排列!
rights_list = [rights_obj.menu_json() for rights_obj in rights_obj_set]
rights_list.sort(key=lambda x: (x["pid"], x["id"]), reverse=True)
print(rights_list)
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
结果像是下面这个样子:(简化了下
[
{'id': 128, 'pid': 124, 'title': '数据分析', 'sort': 4},
{'id': 127, 'pid': 124, 'title': '分析页','sort': 3},
{'id': 126, 'pid': 124, 'title': '首页','sort': 1},
{'id': 125, 'pid': 124, 'title': '工作台','sort': 2},
{'id': 116, 'pid': 100, 'title': '员工管理','sort': 4},
{'id': 111, 'pid': 100, 'title': '部门管理','sort': 1},
{'id': 106, 'pid': 100, 'title': '角色管理','sort': 3},
{'id': 101, 'pid': 100, 'title': '权限管理','sort': 2},
{'id': 124, 'pid': 0, 'title': '工作空间','sort': 1},
{'id': 100, 'pid': 0, 'title': '系统管理','sort': 2}
]
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
构建菜单树形结构: (不是特别好理解) - 可以断点调试,看看样子. 下面是以两层结构为例, 没有用递归..
F8一行行执行代码, option+command+R直接跳到下一个断点处, 跳过去时, 该断点处的这行代码是没有执行的哦!
menu_dict = {}
for menu_item in rights_list:
# 根据父级ID将菜单项添加到字典中
if menu_item["pid"] not in menu_dict:
menu_dict[menu_item["pid"]] = [menu_item]
else:
menu_dict[menu_item["pid"]].append(menu_item)
# 如果菜单项已经有子节点,准备将其子节点移入 > 字典是可变的
if menu_item["id"] in menu_dict:
menu_item["children"] = deepcopy(menu_dict[menu_item["id"]])
menu_item["children"].sort(key=lambda item: item["sort"])
del menu_dict[menu_item["id"]]
# 返回顶级菜单项并按排序字段排序
sorted(menu_dict.get(0, []), key=lambda item: item["sort"])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for循环上面rights_list的数据, 第一条至第8条数据都只会执行for循环里的第一个if语句, 新字典大概长这样
- {'124': [第1条,第2条,第3条,第4条] , '100': [第5条,第6条,第7条,第8条]}
循环到第9条,第10条数据时, 执行的是for循环里的第一个以及第二个if语句, 新字典大概长这样
{ '0': [ 第9条多个了children属性,对应的值是[第1条,第2条,第3条,第4条] , 第10条多个了children属性,对应的值是[第5条,第6条,第7条,第8条] , ] }
1
2
3
4
5
6最后, 取了key值为0对应的value.
最终效果长这样(简化了)
[
{'id': 124, 'pid': 0, 'title': '工作空间', 'sort': 1,'children': [
{'id': 126, 'pid': 124, 'title': '首页', 'sort': 1},
{'id': 125, 'pid': 124, 'title': '工作台', 'sort': 2},
{'id': 127, 'pid': 124, 'title': '分析页', 'sort': 3},
{'id': 128, 'pid': 124, 'title': '数据分析', 'sort': 4}
]},
{'id': 100, 'pid': 0, 'title': '系统管理', 'sort': 2, 'children': [
{'id': 111, 'pid': 100, 'title': '部门管理', 'sort': 1},
{'id': 101, 'pid': 100, 'title': '权限管理', 'sort': 2},
{'id': 106, 'pid': 100, 'title': '角色管理', 'sort': 3},
{'id': 116, 'pid': 100, 'title': '员工管理', 'sort': 4}
]}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14