from app import models
import re
from functools import wraps
from django.shortcuts import HttpResponse, redirect
class PermissionHelper(object):
def __init__(self, request, user):
self.user = user #userID
self.request = request #request对象
self.current_url = request.path_info #获取用户输入的当前路径
self.permission2role_dict = None #权限显示效果{"/index":"add"}
self.menu_leaf_list = None #有url的叶子节点
self.menu_list = None #所有的菜单
self.session_data() #把所有权限注册到session中
def session_data(self):
permission_dict = self.request.session.get("permission_info") #从session中取出数据
if permission_dict: #如果数据在session中存在把数据取出赋值给对象变量
self.permission2role_dict = permission_dict["permission2role_dict"]
self.menu_leaf_list = permission_dict["menu_leaf_list"]
self.menu_list = permission_dict["menu_list"]
else:
#如果session不存在从数据库中取出数据注入到session中
role_list = models.Role.objects.filter(role2userinfo__user=self.user) #取出当前登录用户的所有角色
permission2role_list = models.Permission2Action.objects.filter(permission2action2role__role__in=role_list).
values("p__url", "a__code").distinct() #取出当前登录用户所有角色的url和antion并去重
permission2role_dict = {}
#转换数据格式把数据从queryset中取出存在字典中 权限显示效果{"url":"对应的权限操作"}
for item in permission2role_list:
item = {
"url": item["p__url"],
"code": item["a__code"]
}
if item["url"] in permission2role_dict:
permission2role_dict[item["url"]].append(item["code"])
else:
permission2role_dict[item["url"]] = [item["code"], ]
#取出所有url的叶子节点
menu_leaf_list = list(models.Permission2Action.objects.filter(permission2action2role__role__in=role_list).
exclude(p__menu__isnull=True).values("p__id", "p__title", "p__url",
"p__menu").distinct())
#取出所有菜单
menu_list = list(models.Menu.objects.values("id", "title", "parent_id"))
#把所有权限注入到session中
self.request.session["permission_info"] = {
"permission2role_dict": permission2role_dict,
"menu_leaf_list": menu_leaf_list,
"menu_list": menu_list
}
#第一次登录session中没有数据把数据赋值给对象变量
self.permission2role_dict = permission2role_dict
self.menu_leaf_list = menu_leaf_list
self.menu_list = menu_list
#生成权限菜单显示效果[{id:1,child:[{id:3,child[{id:5,child:[]}]}]}]
def menu_data_list(self):
menu_leaf_dict = {}
open_menu_leaf_parent_id = None
for item in self.menu_leaf_list: #取出所有存在URL的叶子节点
item = {
"id": item["p__id"],
"title": item["p__title"],
"url": item["p__url"],
"parent_id": item["p__menu"],
"child": [], #添加挂靠的列表
"status": True, #添加显示状态
"open": False, #添加菜单的激活状态
}
if re.match(item["url"], self.current_url): #匹配URL是否是当前用户输入的URL
item["open"] = True #如果匹配成功就激活
open_menu_leaf_parent_id = item["parent_id"] #保存当前激活状态的URL的parentid用来匹配上级菜单
if item["parent_id"] in menu_leaf_dict: #更改数据结构结果为{1:["id":...,'child':[]]}
menu_leaf_dict[item["parent_id"]].append(item)
else:
menu_leaf_dict[item["parent_id"]] = [item, ]
menu_dict = {}
for item in self.menu_list: #取出所有的菜单并添加child,显示状态和激活状态
item["child"] = []
item["status"] = False
item["open"] = False
menu_dict[item["id"]] = item #更改数据结构显示为{1:{"id":...},2:{"id":....}}
while open_menu_leaf_parent_id: #更改父节点的激活状态如果是当前激活的URL就把当前URL父节点的所有激活状态改为True
menu_dict[open_menu_leaf_parent_id]["open"] = True
open_menu_leaf_parent_id = menu_dict[open_menu_leaf_parent_id]["parent_id"]
for k, v in menu_leaf_dict.items(): #把所有叶子节点的信息挂靠到父节点的child中
menu_dict[k]["child"] = v
parent_id = k
while parent_id:
menu_dict[parent_id]["status"] = True #更改所有存在叶子节点的父节点的显示状态
parent_id = menu_dict[parent_id]["parent_id"]
result = []
for row in menu_dict.values():
if row["parent_id"]: #遍历菜单把存在parent_id的菜单都挂靠到父节点上
menu_dict[row["parent_id"]]["child"].append(row)
else:
result.append(row)
return result
def menu_content(self, content_list): #递归生成所有子菜单
response = ""
tpl = """
<div class="item %s">
<div class="title">%s</div>
<div class="content">%s</div>
</div>
"""
for row in content_list:
if not row["status"]:
continue
active = ""
if row["open"]:
active = "active"
title = row["title"]
content = self.menu_content(row["child"])
if "url" in row:
response += '<a class="%s" href="%s">%s</a>' % (active, row["url"], row["title"])
else:
response += tpl % (active, title, content)
return response
def menu_tree(self): #生成所有父菜单
response = ""
tpl = """
<div class="item %s">
<div class="title">%s</div>
<div class="content">%s</div>
</div>
"""
for row in self.menu_data_list():
if not row["status"]:
continue
active = ""
if row["open"]:
active = "active"
title = row["title"]
content = self.menu_content(row["child"])
response += tpl % (active, title, content)
return response
def actions(self): #取出当前登录的用户的所有操作权限
action_list = []
for key, value in self.permission2role_dict.items():
if re.match(key, self.current_url):
action_list.append(value)
return action_list
def permission(func): #权限验证装饰器
@wraps(func)
def inner(request, *args, **kwargs):
user_id = request.user.pk
if not user_id:
return redirect("/login/")
permission_obj = PermissionHelper(request, user_id)
action_list = permission_obj.actions()
if not action_list:
return HttpResponse("没有权限访问")
kwargs["menu_str"] = permission_obj.menu_tree()
kwargs["action_list"] = action_list
return func(request, *args, **kwargs)
return inner