舉個例子,一個初創公司中(CEO,產品總監,技術攻城獅,搬磚的)...寥寥幾人,每一個人可能會同時扮演多種角色(每種角色相對應都有必定不一樣的權限)html
那麼,人 角色 權限三者間存在一種怎樣的聯繫(又該怎樣生成數據庫表)數據庫
So,能夠肯定了 用戶表、角色表、權限表session
class Userinfo(models.Model): """ 用戶表 """ nickname = models.CharField(max_length=32) password = models.CharField(max_length=32) def __str__(self): return self.nickname class Role(models.Model): """ 角色表 """ caption = models.CharField(max_length=32) def __str__(self): return self.caption class UserinfoToRole(models.Model): """ 用戶能夠扮演多種角色 角色也能夠對應多個用戶 """ u = models.ForeignKey('Userinfo') r = models.ForeignKey('Role') def __str__(self): return '%s-%s' %(self.u.nickname, self.r.caption) class Url(models.Model): """ 權限Url表 和菜單有外鍵關係 應該掛載在相應的菜單下面 """ caption = models.CharField(max_length=32) url = models.CharField(max_length=32) menu = models.ForeignKey('Menu', null=True, blank=True) def __str__(self): return '%s-%s' %(self.caption, self.url) class Action(models.Model): """ 功能權限表 例如 一、增 二、刪 三、改 四、查 """ caption = models.CharField(max_length=32) code = models.CharField(max_length=32) def __str__(self): return self.code class UrlToAction(models.Model): """ 權限分配小功能 一個URL可能會有增 刪功能,另外一個可能全有 """ url = models.ForeignKey('Url') a = models.ForeignKey('Action') def __str__(self): return '%s-%s' %(self.url.caption, self.a.caption) class RoleToUrlToAction(models.Model): """ 角色分配權限表,功能能夠對應多種角色,角色也能夠對應多種功能 """ uTa = models.ForeignKey('UrlToAction') r = models.ForeignKey('Role') def __str__(self): return '%s-%s' %(self.r.caption, self.uTa) class Menu(models.Model): """ 菜單表 """ caption = models.CharField(max_length=32) m = models.ForeignKey('self', related_name='mTom', null=True, blank=True) def __str__(self): return self.caption
用戶登錄成功以後,經一系列驗證以後......app
能夠經過用戶名去獲取當前登錄用戶扮演了哪些角色,這些角色下面又有哪些權限下的全部功能???數據庫設計
經過上面的表關係,,,大概能夠經過四種方式能夠獲取當前用戶所扮演的角色ide
login_username = request.POST.get('user') # 根據登錄用戶獲取 用戶扮演的角色 login_user = models.Userinfo.objects.filter(nickname=login_username).first() # 方式一 # role_list = models.UserinfoToRole.objects.filter(u=login_user) # 方式二 # role_list = models.Role.objects.filter(userinfotorole__u=login_user) # 方式三 role_list = models.Role.objects.filter(userinfotorole__u__nickname=login_username) # 方式四 # 若是有多對多第三字段,經過多對多字段取 # 前提:m = models.ManyToManyField("Role") # user_obj = models.User.objects.get(username=username) # role_list = user_obj.m.all()
上面代碼中,獲取所扮演的角色列表role_list,接下來應該這麼些角色相應的都有哪些功能(各種權限url下的功能)優化
# 獲取角色下全部的權限 # 我的全部權限都將保存在session中,往後做匹配使用且沒法實時更新,需從新登錄生效 # 方式一 # roleTourlToaction_list = models.RoleToUrlToAction.objects.filter(r__in=role_list) # 方式二 # 不一樣角色可能相對應一樣的功能,故而去重 roleTourlToaction_list = models.UrlToAction.objects.filter(roletourltoaction__r__in=role_list).\ values('url__url', 'a__code').distinct()
如今能夠公開的情報:url
menu_leaf_list = models.UrlToAction.objects.\ filter(roletourltoaction__r__in=role_list).exclude(url__menu__isnull=True).\ values('url__id', 'url__url', 'url__caption', 'url__menu').distinct()
接下來應該構建一些東西了,而且很是巧妙......spa
A、構建權限(葉子節點)字典設計
menu_leaf_dict = {} for item in menu_leaf_list: item = { 'id': item['url__id'], 'url': item['url__url'], 'caption': item['url__caption'], 'parent_id': item['url__menu'], 'child': [] } if item['parent_id'] in menu_leaf_dict: menu_leaf_dict[item['parent_id']].append(item) else: menu_leaf_dict[item['parent_id']] = [item, ] import re if re.match(item['url'], request.path): item['open'] = True open_leaf_parent_id = item['parent_id'] # 此步構建了權限字典(字典的鍵爲菜單的ID,即權限掛載在哪一個菜單下) # 且用正則驗證當前用戶訪問url和權限url進行匹配, 返回成功即爲打開狀態 # print(menu_leaf_dict)
B、構建全部菜單字典
# 獲取全部的菜單列表(每條數據爲一個字典) menu_list = models.Menu.objects.values('id', 'caption', 'm__id') menu_dict = {} for item in menu_list: item['child'] = [] # 爲每一個菜單設置一個孩子列表 item['status'] = False # 是否顯示 item['open'] = False # 是否打開 menu_dict[item['id']] = item # 菜單字典賦值操做 # 此步構建了菜單字典(鍵爲每條菜單的id, 值爲每條菜單數據並附加了一些內容)
C、將Url(權限)掛載在與之對應菜單字典上(找父親啊找父親),生成全新的菜單字典
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'] # 將已經選中的菜單標記爲【展開】 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'] # 此步將權限(url)掛載到了菜單的最後一層 # 而且將權限的全部直接父級的status改成了True !妙哉 # 再且若用戶當前訪問Url與權限(url)匹配,open則爲打開狀態 !妙哉妙哉 # 返回了全新的菜單字典 # print(menu_dict)
D、處理等級關係,場景應用:層級評論...
result = [] for row in menu_dict.values(): if not row['parent_id']: # 表示爲根級菜單 result.append(row) else: # 子級菜單相應的去父菜單的child下面 menu_dict[row['parent_id']]['child'].append(row) print(result) # 此步將全部的層級關係作了處理,造成簡潔明瞭的樹形結構
E、頁面HTML顯示菜單層級顯示(遞歸實現)
response = '' tpl = """ <div class="item {0}"> <div class="title">{1}</div> <div class="content">{2}</div> </div> """ for row in result: # 若是狀態爲False,則不顯示 if not row['status']: continue active = '' if row['open']: print('ok') active = 'active' title = row['caption'] content = menu_cotent(row['child']) response += tpl.format(active, title, content) return render(request, 'index.html', {'response': response}) def menu_cotent(child_list): """ 遞歸生成html :param child_list: 子級列表 :return: """ response = '' tpl = """ <div class="item {0}"> <div class="title">{1}</div> <div class="content">{2}</div> </div> """ for row in child_list: if not row['status']: # status continue active = '' if row['open']: # open_leaf_parent_id active = 'active' if 'url' in row: # 若是url存在於row中, 則表示到了最終權限節點 response += """<a href="%s" class="%s">%s</a>""" \ %(row['url'], active, row['caption']) else: title = row['caption'] content = menu_cotent(row['child']) response += tpl.format(active, title, content) return response
F、優化(類之整理)
上述代碼貌似看起來很繁瑣,So!下面代碼將上文中數據構建及生成多級菜單封裝到了類裏面
class MenuHelper(object): def __init__(self, request, username): # 當前請求的request對象 self.request = request # 當前登錄的用戶 self.username = username # 當前訪問Url self.current_url = request.path # 獲取當前用戶的全部權限 self.permission2action_dict = None # 獲取在菜單中顯示的權限 self.menu_leaf_list = None # 獲取全部菜單 self.menu_list = None self.session_data() def session_data(self): # 獲取用戶的全部權限信息, 做用於用戶訪問 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'] else: # 獲取當前登錄用戶全部角色 role_list = models.Role.objects.filter(userinfotorole__u=self.username) # 獲取角色的全部行爲列表 roleTourlToaction_list = models.UrlToAction.objects.filter(roletourltoaction__r__in=role_list).\ values('url__url', 'a__code').distinct() # 構建行爲字典 roleTourlToaction_dict = {} for item in roleTourlToaction_list: if item['url__url'] in roleTourlToaction_dict: roleTourlToaction_dict[item['url__url']].append(item['a__code']) else: roleTourlToaction_dict[item['url__url']] = [item['a__code'], ] # 獲取菜單的葉子節點, 即顯示在菜單的最後一層 menu_leaf_list = models.UrlToAction.objects. \ filter(roletourltoaction__r__in=role_list).exclude(url__menu__isnull=True). \ values('url__id', 'url__url', 'url__caption', 'url__menu').distinct() # 獲取全部的菜單列表 menu_list = models.Menu.objects.values('id', 'caption', 'parent_id') self.request.session['permission_info'] = { 'permission2action_dict': roleTourlToaction_dict, 'menu_leaf_list': menu_leaf_list, 'menu_list': menu_list } self.permission2action_dict = roleTourlToaction_dict self.menu_leaf_list = menu_leaf_list self.menu_list = menu_list def menu_data(self): menu_leaf_dict = {} open_leaf_parent_id = None # 歸併全部的葉子節點 for item in self.menu_leaf_list: item = { 'id': item['url__id'], 'url': item['url__url'], 'caption': item['url__caption'], 'parent_id': item['url__menu'], 'child': [], 'status': False, '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, ] import re if re.match(item['url'], self.current_url): item['open'] = True open_leaf_parent_id = item['parent_id'] # 生成菜單字典 menu_dict = {} for item in self.menu_list: item['child'] = [] item['status'] = False item['open'] = False menu_dict[item['id']] = item # 將葉子節點添加到菜單字典中... 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'] # 將已經選中的菜單標記爲【展開】 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'] # 生成樹形結構數據 result = [] for row in menu_dict.values(): if not row['parent_id']: result.append(row) else: menu_dict[row['parent_id']]['child'].append(row) return result def menu_tree(self): response = '' tpl = """ <div class="item {0}"> <div class="title">{1}</div> <div class="content">{2}</div> </div> """ result = self.menu_data() for row in result: if not row['status']: continue active = '' if row['open']: print('ok') active = 'active' title = row['caption'] content = self.menu_cotent(row['child']) response += tpl.format(active, title, content) return response def menu_cotent(self, child_list): response = '' tpl = """ <div class="item {0}"> <div class="title">{1}</div> <div class="content">{2}</div> </div> """ for row in child_list: if not row['status']: # status continue active = '' if row['open']: # open_leaf_parent_id active = 'active' if 'url' in row: # 若是url存在於row中, 則表示到了最終權限節點 response += """<a href="%s" class="%s">%s</a>""" \ % (row['url'], active, row['caption']) else: title = row['caption'] content = self.menu_cotent(row['child']) response += tpl.format(active, title, content) return response
Views
class MenuHelper(object): def __init__(self, request, username, current_url): # 當前請求的request對象 self.request = request # 當前登錄的用戶 self.username = username # 當前訪問Url self.current_url = current_url # 獲取當前用戶的全部權限 self.permission2action_dict = None # 獲取在菜單中顯示的權限 self.menu_leaf_list = None # 獲取全部菜單 self.menu_list = None self.session_data() def session_data(self): # 獲取用戶的全部權限信息, 做用於用戶訪問 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'] else: # 獲取當前登錄用戶全部角色 role_list = models.Role.objects.filter(userinfotorole__u__nickname=self.username) # 獲取角色的全部行爲列表 roleTourlToaction_list = list(models.UrlToAction.objects.filter(roletourltoaction__r__in=role_list).\ values('url__url', 'a__code').distinct()) # 構建行爲字典 roleTourlToaction_dict = {} for item in roleTourlToaction_list: if item['url__url'] in roleTourlToaction_dict: roleTourlToaction_dict[item['url__url']].append(item['a__code']) else: roleTourlToaction_dict[item['url__url']] = [item['a__code'], ] # 獲取菜單的葉子節點, 即顯示在菜單的最後一層 menu_leaf_list = list(models.UrlToAction.objects. \ filter(roletourltoaction__r__in=role_list).exclude(url__menu__isnull=True). \ values('url__id', 'url__url', 'url__caption', 'url__menu').distinct()) # 獲取全部的菜單列表 menu_list = list(models.Menu.objects.values('id', 'caption', 'parent_id')) self.request.session['permission_info'] = { 'permission2action_dict': roleTourlToaction_dict, 'menu_leaf_list': menu_leaf_list, 'menu_list': menu_list } self.permission2action_dict = roleTourlToaction_dict self.menu_leaf_list = menu_leaf_list self.menu_list = menu_list def menu_data(self): menu_leaf_dict = {} open_leaf_parent_id = None # 歸併全部的葉子節點 for item in self.menu_leaf_list: item = { 'id': item['url__id'], 'url': item['url__url'], 'caption': item['url__caption'], 'parent_id': item['url__menu'], '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, ] import re if re.match(item['url'], self.current_url): item['open'] = True open_leaf_parent_id = item['parent_id'] # 生成菜單字典 menu_dict = {} for item in self.menu_list: item['child'] = [] item['status'] = False item['open'] = False menu_dict[item['id']] = item # 將葉子節點添加到菜單字典中... 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'] print(menu_dict) # 將已經選中的菜單標記爲【展開】 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'] # 生成樹形結構數據 result = [] for row in menu_dict.values(): if not row['parent_id']: result.append(row) else: menu_dict[row['parent_id']]['child'].append(row) return result def menu_tree(self): response = '' tpl = """ <div class="item {0}"> <div class="title">{1}</div> <div class="content">{2}</div> </div> """ result = self.menu_data() for row in result: if not row['status']: continue active = '' if row['open']: active = 'active' title = row['caption'] content = self.menu_cotent(row['child']) response += tpl.format(active, title, content) return response def menu_cotent(self, child_list): response = '' tpl = """ <div class="item {0}"> <div class="title">{1}</div> <div class="content">{2}</div> </div> """ for row in child_list: if not row['status']: # status continue active = '' if row['open']: # open_leaf_parent_id active = 'active' if 'url' in row: # 若是url存在於row中, 則表示到了最終權限節點 response += """<a href="%s" class="%s">%s</a>""" \ % (row['url'], active, row['caption']) else: title = row['caption'] content = self.menu_cotent(row['child']) response += tpl.format(active, title, content) return response def login(request): user_request_url = '/girl.html' login_user = request.GET.get('user') obj = MenuHelper(request, login_user, user_request_url) string = obj.menu_tree() return render(request, 'index.html',{'menu_string': string})
Html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .content{ margin-left: 20px; display: none; } .content a{ display: block; } .active>.content{ display: block; } </style> </head> <body> {{ menu_string | safe }} </body> </html>
經過更改URL(至關於用戶訪問的權限url)從而看到顯示的菜單權限
更新中...