一:表結構(共八張表)
from django.db import models # Create your models here. class User(models.Model): username = models.CharField(max_length=32) pasword = models.CharField(max_length=32) class Meta: verbose_name_plural = "用戶表" def __str__(self): return self.username class Role(models.Model): caption = models.CharField(max_length=32) class Meta: verbose_name_plural = "角色表" def __str__(self): return self.caption class User2Role(models.Model): u = models.ForeignKey("User") r = models.ForeignKey("Role") class Meta: verbose_name_plural = "用戶角色分配表" def __str__(self): return "%s:%s"%(self.u.username,self.r.caption) class Action(models.Model): #/user.html?t=get 獲取用戶信息 #/user.html?t=post 創建用戶 #/user.html?t=put 修改用戶 #/user.html?t=delete 刪除用戶 #1 ---> get 查詢 #2 ---> post 增加 #3 ---> put 修改 #4 ---> delete 刪除 caption = models.CharField(max_length=32) code = models.CharField(max_length=32) class Meta: verbose_name_plural = "操作表" def __str__(self): return self.caption class Menu(models.Model): caption = models.CharField(max_length=32) parent = models.ForeignKey("self",related_name="p",null=True,blank=True) class Meta: verbose_name_plural="菜單表" def __str__(self): return self.caption class Permission(models.Model): #/user.html 用戶管理 caption = models.CharField(max_length=32) url = models.CharField(max_length=32) menu = models.ForeignKey("Menu",null=True) class Meta: verbose_name_plural = "權限" def __str__(self): return self.caption class Permission2Action(models.Model): p = models.ForeignKey("Permission") a = models.ForeignKey("Action") class Meta: verbose_name_plural = "權限表" def __str__(self): return "%s-%s:%s?t=%s"%(self.p.caption,self.a.caption,self.p.url,self.a.code) class Permission2Action2Role(models.Model): p2a = models.ForeignKey("Permission2Action") r = models.ForeignKey("Role") class Meta: verbose_name_plural = "角色權限分配表" def __str__(self): return "%s:%s"%(self.r.caption,self.p2a)
二:登錄業務
def login(request): if request.method == "GET": return render(request,"login.html") else: username = request.POST.get("username") pasword = request.POST.get("password") obj = models.User.objects.filter(username=username,pasword=pasword).get() if obj: #獲取用戶信息,放在session中 request.session['user_info'] = {'nid':obj.id,'username':obj.username} #獲取所有權限 #獲取在菜單中顯示的權限 #獲取所有菜單 #放置在session MenuHelper(request,username) return redirect('/index.html') else: return redirect('/login.html')
三:權限類生成(進行權限和菜單的業務處理)
class MenuHelper(object): def __init__(self,request,username): #當前請求的request self.request = request #當前用戶名 self.username = username #獲取當前url self.current_url = request.path_info #獲取當前用戶的所有權限 self.permission2action_dict = {} #{'url':["操作列表"],} #菜單的葉子節點,即:菜單的最后一層應該(含有權限url)顯示的權限,對應上面的permission2action_dict self.menu_leaf_list = [] #獲取所有菜單對象 self.menu_list = [] #初始化數據,將數據放入session中 self.session_data() #注:只進行數據處理(數據最后是基礎類型),不進行業務邏輯(目標就是將數據放在session中) def session_data(self): #(0)先在session中查看數據是否已經存在 permission_dict = self.request.session.get("permission_info") if permission_dict: self.permission2action_dict = permission_dict['permission2action_dict'] self.menu_leaf_list = permission_dict['menu_leaf_list'] self.menu_list = permission_dict['menu_list'] return #獲取所有角色 r_list = models.Role.objects.filter(user2role__u__username=self.username).all() #獲取角色下的所有權限操作,存放方式{'url':["操作列表"],}便於查找 --> 例如 {'user.html':['get','post'],} p2a_list = models.Permission2Action.objects.filter(permission2action2role__r__in=r_list) #權限操作去重,使用集合去重 p2a_list = set(p2a_list) #(1)循環權限操作列表,將其變為{'user.html':['get','post'],}格式,然后加入self.permission2action_dict for p2a in p2a_list: # print(p2a.p.url) if self.permission2action_dict.get(p2a.p.url) == None: self.permission2action_dict[p2a.p.url] = [] self.permission2action_dict[p2a.p.url].append(p2a.a.code) #上面是用戶擁有的所有權限,用於權限校驗,下面這個是顯示在菜單最后一層的權限(也只有最后一層才會有url權限) #(2)獲取菜單的葉子節點,即:菜單的最后一層應該顯示的權限 self.menu_leaf_list = list( models.Permission2Action.objects. \ filter(permission2action2role__r__in=r_list).exclude(p__menu__isnull=True). \ values('p_id','p__url','p__caption','p__menu').distinct() ) #(3)獲取所有的菜單全部,(根據上面的菜單葉子節點,以及下面的所有菜單,進行比較,我們可以獲取到所有應該顯示的菜單) self.menu_list = list(models.Menu.objects.values('id','caption','parent_id')) #(4)將上面獲取的數據,放入session中 self.request.session['permission_info'] = { 'permission2action_dict':self.permission2action_dict, 'menu_leaf_list':self.menu_leaf_list, 'menu_list':self.menu_list, } #生成菜單樹形結構(包括其他數據) def menu_data_list(self): menu_leaf_dict = {} #形式 # { # '父id':['子節點','子節點',], # '父id':['子節點','子節點',] # } open_leaf_parent_id = None #(1)歸並所有葉子節點 for item in self.menu_leaf_list: item = { 'id':item['p_id'], 'url':item['p__url'], 'caption':item['p__caption'], 'parent_id':item['p__menu'], #對應的是菜單id,可以看出,每個葉子節點都是掛在菜單節點下面,我們只需獲取菜單的樹形結構,將權限葉子節點掛載上去就可以 'child':[], 'status':True, #是否顯示 'open':False #是否展開 } if item['parent_id'] in menu_leaf_dict: menu_leaf_dict[item['parent_id']].append(item) else: menu_leaf_dict[item['parent_id']] = [item,] if re.match(item['url'],self.current_url): #若是當前訪問的url,則打開 item['open'] = True open_leaf_parent_id = item['parent_id'] #(2)獲取所有菜單字典 menu_dict = {} #形式也是 # { # '菜單id':'對應數據處理的字典', # '菜單id': '對應數據處理的字典', # '菜單id': '對應數據處理的字典', # } for item in self.menu_list: item['child'] = [] item['status'] = False #是否顯示,只有擁有權限的菜單,才會展示給用戶,其他的不會顯示 item['open'] = False #和權限葉子節點一致,展開就是一級一級顯示,其他閉合 menu_dict[item['id']] = item #根據上面的全部菜單和歸並后的葉子節點一起獲取我們真正需要的菜單 #(3)將葉子節點添加到菜單中 for k,v in menu_leaf_dict.items(): 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'] #(4)將菜單中已經選中的菜單標記為展開(一級一級展開) while open_leaf_parent_id: menu_dict[open_leaf_parent_id]['open'] = True open_leaf_parent_id = menu_dict[open_leaf_parent_id]['parent_id'] #(5)對所有菜單,進行樹形結構生成,不需要考慮其他的因素(例如是否是葉子節點,是否有權限) #我們只需要判斷狀態status是否為True,然后篩選即可 result = [] for row in menu_dict.values(): #所有菜單進行樹形排序 if not row['parent_id']: result.append(row) # 注意:基礎-->列表賦值的時候默認是淺拷貝,所以無論是是么時候添加到result中,后面的操作對於result數據也是有效d的 else: menu_dict[row['parent_id']]['child'].append(row) #(6)返回樹形結構 return result #獲取子菜單列表的子菜單列表(遞歸模式) def menu_content(self,child_list): response = "" # 菜單模板HTML tpl = """ <div class="item %s"> <div class="title">%s</div> <div class="content">%s</div> </div> """ for row in child_list: if not row['status']: continue active = "" if row['open']: active = "active" if 'url' in row: response += "<a class='%s' href='%s'>%s</a>"%(active,row['url'],row['caption']) else: title = row['caption'] content = self.menu_content(row['child']) response += tpl % (active,title,content) return response def menu_tree(self): response = "" # 菜單模板HTML tpl = """ <div class="item %s"> <div class="title">%s</div> <div class="content">%s</div> </div> """ for row in self.menu_data_list(): #獲取函數返回的樹形結構,進行HTML處理,這里全是根目錄菜單 if not row['status']: #對於不需要顯示的,不做處理 continue active = "" if row['open']: #對於展開的,我們要設置其狀態為active active = "active" title = row['caption'] #獲取其子代的HTML,放在content中 content = self.menu_content(row['child']) #默認子代是列表 response += tpl % (active,title,content) return response def actions(self): ''' 檢測當前用戶是否對當前url有權限訪問,並獲取對當前url有什么權限 :return: ''' action_list = [] #當前數據中保存的權限是{'user.html':['get','post'],}格式,在self.permission2action_dict中 for k,v in self.permission2action_dict.items(): if re.match(k,self.current_url): action_list = v break return action_list
四:生成裝飾器(用於用戶的驗證,使用了上面類)
#設置裝飾器 def permission(func): def inner(request,*args,**kwargs): user_info = request.session.get('user_info') if not user_info: return redirect("/login.html") obj = MenuHelper(request,user_info['username']) action_list = obj.actions() if not action_list: return HttpResponse("無權限訪問") kwargs['menu_string'] = obj.menu_tree() kwargs['action_list'] = action_list return func(request,*args,**kwargs) return inner
五:裝飾器使用
@permission def index(request,*args,**kwargs): acion_list = kwargs.get('action_list') menu_string = kwargs.get("menu_string") if str(request.method).lower() in acion_list: result = "數據,可以訪問" else: result = "沒有數據,無權限訪問" return render(request,'index.html',{'menu_string':menu_string,'action_list':acion_list,'result':result})
六:退出業務
def logout(request): request.session.clear() return redirect('/login.html')
七:前台模板,以及效果展示
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .item{ margin-left: 24px; } .item a{ margin-left: 24px; } </style> </head> <body> <div> <span>菜單顯示</span> {{ menu_string|safe }} </div> <div> <span>結果數據</span> {{ result }} </div> </body> </html>