Django 权限管理-后台根据用户权限动态生成菜单
Django權(quán)限管理
實現(xiàn)目標:
1、管理用戶,添加角色,用戶關(guān)聯(lián)角色
2、添加權(quán)限、角色關(guān)聯(lián)權(quán)限
3、添加動作、權(quán)限關(guān)聯(lián)動作
4、添加菜單、權(quán)限關(guān)聯(lián)菜單
實現(xiàn)動態(tài)生成用戶權(quán)限菜單(可設(shè)置多級菜單嵌套)、根據(jù)前臺URL自動選中菜單并折疊其余菜單
?
最終實現(xiàn)類似這樣的效果:
菜單一 菜單1.1 菜單1.2 菜單1.2.1 訂單管理 分類管理菜單二
?
?
一、首先是建立表格
models
from django.db import models# Create your models here. class User(models.Model):username = models.CharField(max_length=32)password = models.CharField(max_length=64)class Meta:verbose_name_plural = '用戶表'def __str__(self):return self.usernameclass Role(models.Model):role = models.CharField(max_length=32)class Meta:verbose_name_plural = '角色表'def __str__(self):return self.roleclass User2Role(models.Model):u = models.ForeignKey(User, on_delete=models.CASCADE)r = models.ForeignKey(Role, on_delete=models.CASCADE)class Meta:verbose_name_plural = '用戶分配角色'def __str__(self):return '%s-%s' % (self.u.username, self.r.role)class Menu(models.Model):caption = models.CharField(max_length=32)parent = models.ForeignKey('self', related_name='p', null=True, blank=True, on_delete=models.CASCADE)def __str__(self):return '%s' % (self.caption,)class Permission(models.Model):caption = models.CharField(max_length=32)url = models.CharField(max_length=32)menu = models.ForeignKey(Menu, null=True, blank=True, on_delete=models.CASCADE)class Meta:verbose_name_plural = 'URL表'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)class Meta:verbose_name_plural = '操作表'def __str__(self):return self.captionclass Permission2Action(models.Model):p = models.ForeignKey(Permission, on_delete=models.CASCADE)a = models.ForeignKey(Action, on_delete=models.CASCADE)class Meta:verbose_name_plural = '權(quán)限表'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, on_delete=models.CASCADE)r = models.ForeignKey(Role, on_delete=models.CASCADE)class Meta:verbose_name_plural = '角色分配權(quán)限'def __str__(self):return '%s=>%s' % (self.r.role, self.p2a)?
建立表后,用django的admin在表中添加一些數(shù)據(jù)
1、用戶表:建立幾個用戶
2、角色表:建立幾個角色,如:CEO\CTO\開發(fā)\客服\業(yè)務(wù)員
3、給用戶分配角色
4、URL表:建立幾個管理菜單,如:分類管理\報表管理\訂單管理\用戶管理
5、操作表:增\刪\改\查
6、權(quán)限表:給URL添加操作內(nèi)容
7、角色分配權(quán)限:
8、菜單表:設(shè)置三層菜單,如:菜單一:菜單1.1:菜單1.1.1
?
?
二、url
添加數(shù)據(jù)后建立url
urlpatterns = [path('admin/', admin.site.urls),path('login/', views.login),path('logout/', views.logout),path('index/', views.index), ]?
三、login
編輯login,登錄成功后,將用戶信息保存在session中,并通過MenuHelper類獲取用戶的權(quán)限和菜單,也保存在session中,實例化MenuHelper類
def login(request):if request.method == "GET":return render(request, 'login.html')else:username = request.POST.get('username')pwd = request.POST.get('pwd')obj = models.User.objects.filter(username=username, password=pwd).first()if obj:# 登錄成功,獲取當前用戶信息# 放到session中request.session['user_info'] = {'nid': obj.id, 'username': obj.username}# 獲取當前用戶的所有權(quán)限,獲取所有菜單,獲取在菜單中顯示的權(quán)限(葉子節(jié)點)# 放到session中 MenuHelper(request, obj.username)return redirect('/index')else:return redirect('/login')?
四、MenuHelper類
首先獲取當前用戶,通過reques.path_info獲取當前用戶訪問的url
調(diào)用類的session_data方法:判斷該用戶當前session中是否已經(jīng)有內(nèi)容,如果有內(nèi)容則與取出session中的內(nèi)容,否則通過用戶名分別獲取當前用戶的角色列表、權(quán)限列表、最終顯示的菜單列表以及所有菜單,隨后跳轉(zhuǎn)至index
class MenuHelper(object):def __init__(self, request, username):# 當前請求的request對象self.request = request# 當前用戶名self.username = username# 當前url,如用戶訪問127.0.0.1:8000/index.html?p=123 會獲得:index.htmlself.current_url = request.path_info# 當前用戶的所有權(quán)限self.permission2action_dict = None# 當前用戶菜單中顯示的所有權(quán)限(葉子節(jié)點)self.menu_leaf_list = None# 所有菜單self.menu_list = Noneself.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(user2role__u__username=self.username)# 獲取當前用戶的權(quán)限列表(url+action)permission2action_list = models.Permission2Action.objects.\filter(permission2action2role__r__in=role_list).\values('p__url', 'a__code').distinct()permission2action_dict = {}for item in permission2action_list:if item['p__url'] in permission2action_dict:permission2action_dict[item['p__url']].append(item['a__code'])else:permission2action_dict[item['p__url']] = [item['a__code'], ]# 獲取菜單的葉子節(jié)點,即:菜單的最后一層應(yīng)該顯示的權(quán)限menu_leaf_list = list(models.Permission2Action.objects.filter(permission2action2role__r__in=role_list).exclude(p__menu__isnull=True).values('p_id', 'p__url', 'p__caption', 'p__menu').distinct())# 獲取所有菜單列表menu_list = list(models.Menu.objects.values('id', 'caption', 'parent_id'))self.request.session['permission_info'] = {'permission2action_dict': permission2action_dict,'menu_leaf_list': menu_leaf_list,'menu_list': menu_list,}# self.permission2action_list = permission2action_list# self.menu_leaf_list = menu_leaf_list# self.menu_list = menu_listdef menu_data_list(self):# 設(shè)置一個空的葉子節(jié)點字典menu_leaf_dict = {}# 首先設(shè)置葉子父id節(jié)點為空open_left_parent_id = Nonefor item in self.menu_leaf_list:# 將獲取的葉子節(jié)點列表的每一個值轉(zhuǎn)換為字典形式,并重新設(shè)置key,添加child,status,open字段item = {'id': item['p_id'],'url': item['p__url'],'caption': item['p__caption'],'parent_id': item['p__menu'],'child': [],'status': True, # 是否顯示'open': False, # 是否打開 }# 判斷每一個葉子節(jié)點的父節(jié)點,將每個葉子節(jié)點的內(nèi)容添加至父節(jié)點id作為的key中# 判斷父節(jié)點id作為的key是否在葉子節(jié)點字典中存在,如果存在,則將item值append進入if item['parent_id'] in menu_leaf_dict:menu_leaf_dict[item['parent_id']].append(item)# 如果不存在,則直接在列表中生成一個key是葉子節(jié)點父節(jié)點id的,值為item的數(shù)據(jù)else:menu_leaf_dict[item['parent_id']] = [item, ]# 判斷用戶輸入的url是否與現(xiàn)在的url匹配,item['url']可以寫成一個正則表達式,用match進行匹配# 如果匹配上,將葉子節(jié)點的open置為true,并將葉子節(jié)點的父節(jié)點id進行賦值import reif re.match(item['url'], self.current_url):item['open'] = Trueopen_left_parent_id = item['parent_id']# 設(shè)置一個菜單空字典menu_dict = {}# 將菜單列表轉(zhuǎn)換為字典,并增加child,status,open字段# 將列表中的id作為key,列表中的值作為值for item in self.menu_list:item['child'] = []item['status'] = Falseitem['open'] = Falsemenu_dict[item['id']] = item# 循環(huán)葉子字典,設(shè)置菜單字典中對應(yīng)的child內(nèi)容為葉子字典的值for k, v in menu_leaf_dict.items():menu_dict[k]['child'] = v# 設(shè)置菜單字典的parent_id的值為葉子字典的key(也就是葉子中的parent)parent_id = k# 設(shè)置菜單字典中的status狀態(tài)為True,并循環(huán)設(shè)置父級菜單的status為Truewhile parent_id:menu_dict[parent_id]['status'] = Trueparent_id = menu_dict[parent_id]['parent_id']# 判斷葉子父級id,將open設(shè)置為True,并循環(huán)設(shè)置父級菜單的open為Truewhile open_left_parent_id:menu_dict[open_left_parent_id]['open'] = Trueopen_left_parent_id = menu_dict[open_left_parent_id]['parent_id']# print('循環(huán)權(quán)限用戶url字典,將用戶權(quán)限取得的id匹配菜單列表id并設(shè)置["child"]值為用戶權(quán)限內(nèi)容')# print('設(shè)置parent_id變量為:用戶權(quán)限url的id')# print('如果有,菜單id的["status"]設(shè)置為True')# print('并且將parent_id的值設(shè)置為:菜單字典中菜單id的["parent"],等待下一次循環(huán)')# for k, v in menu_dict.items():# print(k, v)# #####################處理菜單的等級關(guān)系########################## menu_dict 應(yīng)用:多級評論,多級菜單 result = []# 按父子關(guān)系,將菜單列表中的值,層疊放入一個result中# 這里需要注意的是,只需要尋找一層的父id,并將自己放入,無需一層一層尋找到上一層的父節(jié)點。for row in menu_dict.values():if not row['parent_id']:result.append(row)else:menu_dict[row['parent_id']]['child'].append(row)return resultdef menu_content(self, child_list):response = ''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']:continueactive = ''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 responsedef 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']:continueactive = ''if row['open']:active = 'active'title = row['caption']content = self.menu_content(row['child'])response += tpl % (active, title, content)return responsedef action(self):"""檢查當前用戶是否對當前URL有訪問權(quán),并獲取對當前URL有什么權(quán)限:return:"""action_list = []for k, v in self.permission2action_dict.items():if re.match(k, self.current_url):action_list = vbreakreturn action_list?
五、index
index使用了一個裝飾器,判斷用戶session中是否有用戶信息,如果有用戶信息,使用MenuHelper類實例化一個對象,調(diào)用對象的action方法,獲得action_list
如果列表為空則返回無權(quán)訪問
否則返回菜單樹(調(diào)用了類的menu_data_list方法,循環(huán)遞歸的生成菜單樹,返回的是后臺生成的html代碼)
以及權(quán)限列表
最后通過權(quán)限列表中的內(nèi)容分別進行操作,并返回至前臺(這個位置沒有編寫完成,僅僅寫了一個舉例)
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.action()if not action_list:return HttpResponse('無權(quán)限訪問')kwargs['menu_string'] = obj.menu_tree()kwargs['action_list'] = action_listreturn func(request, *args, **kwargs)return inner@permission def index(request, *args, **kwargs):actions_list = kwargs.get('actions_list')menu_string = kwargs.get('menu_string')if "GET" in actions_list:result = models.User.objects.all()else:result = []return render(request, 'index.html', {'menu_string': menu_string,'actions_list': actions_list,'result': result,})?
六、login.html
?
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body><form action="/login/" method="POST">{% csrf_token %}<input type="text" name="username" /><input type="text" name="pwd" /><input type="submit" value="提交" /></form> </body> </html>?
?
?
七、index.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><div style="float: left;width: 20%;">{{ menu_string|safe }}</div><div style="float: left;width: 80%;"></div></body> </html>?
轉(zhuǎn)載于:https://www.cnblogs.com/trunkslisa/p/9667029.html
總結(jié)
以上是生活随笔為你收集整理的Django 权限管理-后台根据用户权限动态生成菜单的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Centos:mysql的安装和使用:y
- 下一篇: 敏捷世界中的合规性