django自定義rbac許可權元件(二級選單)
阿新 • • 發佈:2018-12-27
一、目錄結構
二、表結構設計
model.py
from django.db import models # Create your models here. class Menu(models.Model): """選單表 一級選單""" title = models.CharField(max_length=32) icon = models.CharField(max_length=64, null=True, blank=True, verbose_name='圖示') def __str__(self):return self.title class Permission(models.Model): """ 許可權表 可以做二級選單的許可權 menu 關聯 選單表 不可以做選單的許可權 menu=null """ url = models.CharField(max_length=32, verbose_name='許可權') title = models.CharField(max_length=32, verbose_name='標題') menu = models.ForeignKey("Menu",null=True, blank=True, verbose_name="所屬選單",on_delete=models.CASCADE) class Meta: # 這個選項是指定,模型的複數形式是什麼,比如: # verbose_name_plural = "學校" # 如果不指定Django會自動在模型名稱後加一個’s’ verbose_name_plural = '許可權表' verbose_name = '許可權' def __str__(self):return self.title class Role(models.Model): """ 角色表 """ name = models.CharField(max_length=32, verbose_name='名稱') permissions = models.ManyToManyField('Permission', verbose_name='角色擁有的許可權',blank=True) def __str__(self): return self.name class User(models.Model): """ 使用者表 """ name = models.CharField(max_length=32, verbose_name='名稱') password = models.CharField(max_length=32, verbose_name='密碼') roles = models.ManyToManyField('Role', verbose_name='使用者擁有的角色',blank=True) def __str__(self): return self.name
三、許可權資訊初始化
使用者登陸成功後保留許可權資訊與選單資訊
service.permission.py
from django.conf import settings def init_permisson(request, obj): """ 許可權資訊的初識化 儲存許可權和選單的資訊 :param request: :param obj: :return: """ # 登陸成功,儲存許可權的資訊(可能存在建立了角色沒有分配許可權,有的使用者擁有多個角色許可權重複的要去重.distinct()) ret = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url', 'permissions__title', 'permissions__menu__title', 'permissions__menu__icon', 'permissions__menu_id' ).distinct() # 存放許可權資訊 permission_list = [] # 存放選單資訊 menu_dict = {} for item in ret: # 將所有的許可權資訊新增到permission_list permission_list.append({'url': item['permissions__url']}) # 構造選單的資料結構 menu_id = item.get('permissions__menu_id') # 表示當前的許可權是不做選單的許可權 if not menu_id: continue # 可以做選單的許可權 if menu_id not in menu_dict: menu_dict[menu_id] = { 'title': item['permissions__title'], # 一級選單標題 'icon': item['permissions__menu__icon'], 'children': [ {'title': item['permissions__menu__title'], 'url': item['permissions__url']}, ] } else: menu_dict[menu_id]['children'].append( {'title': item['permissions__menu__title'], 'url': item['permissions__url']}) # print(menu_dict) # 保留許可權資訊到session(因為session可以存到記憶體中,提高工作效率)中 request.session[settings.PERMISSION_SESSION_KEY] = permission_list # 儲存選單資訊 request.session[settings.PERMISSION_MENU_KEY] = menu_dict
四、中介軟體中許可權校驗
選單資料結構構造
注意構造選單的資料結構,將查詢出的元資料構造為分級的資料結構。
# 元資料 data = [{ 'permissions__url': '/customer/list/', 'permissions__title': '客戶列表', 'permissions__menu__title': '資訊列表', 'permissions__menu__icon': 'fa-code-fork', 'permissions__menu_id': 1 }, { 'permissions__url': '/customer/list/', 'permissions__title': '使用者列表', 'permissions__menu__title': '資訊列表', 'permissions__menu__icon': 'fa-code-fork', 'permissions__menu_id': 1 }, { 'permissions__url': '/customer/add/', 'permissions__title': '增加客戶', 'permissions__menu__title': None, 'permissions__menu__icon': None, 'permissions__menu_id': None }, { 'permissions__url': '/customer/edit/(\\d+)/', 'permissions__title': '編輯客戶', 'permissions__menu__title': None, 'permissions__menu__icon': None, 'permissions__menu_id': None }] # 目標資料 { 1:{ 'title':'資訊列表', 'icon':'fa-code-fork', 'children': [ {'title': '客戶列表','url':'/customer/list/ }, {'title': '使用者列表','url':'/customer/list/ } ] } }
middlewares.rbac.py
from django.conf import settings def init_permisson(request, obj): """ 許可權資訊的初識化 儲存許可權和選單的資訊 :param request: :param obj: :return: """ # 登陸成功,儲存許可權的資訊(可能存在建立了角色沒有分配許可權,有的使用者擁有多個角色許可權重複的要去重.distinct()) ret = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url', 'permissions__title', 'permissions__menu__title', 'permissions__menu__icon', 'permissions__menu_id' ).distinct() # 存放許可權資訊 permission_list = [] # 存放選單資訊 menu_dict = {} for item in ret: # 將所有的許可權資訊新增到permission_list permission_list.append({'url': item['permissions__url']}) # 構造選單的資料結構 menu_id = item.get('permissions__menu_id') # 表示當前的許可權是不做選單的許可權 if not menu_id: continue # 可以做選單的許可權 if menu_id not in menu_dict: menu_dict[menu_id] = { 'title': item['permissions__title'], # 一級選單標題 'icon': item['permissions__menu__icon'], 'children': [ {'title': item['permissions__menu__title'], 'url': item['permissions__url']}, ] } else: menu_dict[menu_id]['children'].append( {'title': item['permissions__menu__title'], 'url': item['permissions__url']}) # print(menu_dict) # 保留許可權資訊到session(因為session可以存到記憶體中,提高工作效率)中 request.session[settings.PERMISSION_SESSION_KEY] = permission_list # 儲存選單資訊 request.session[settings.PERMISSION_MENU_KEY] = menu_dict
五、自定義inclusion_tag
用於定義html片段,實現動態資料傳入。
檔案包必須叫templatetags
templatetags.rbac.py
from django import template from django.conf import settings import re register = template.Library() @register.inclusion_tag('rbac/menu.html') def menu(request): # # 取到存在session 裡的選單許可權資訊資訊(一級選單時) # menu_list = request.session.get(settings.PERMISSION_MENU_KEY) # # for item in menu_list: # # 正則匹配當前路徑 # if re.match('^{}$'.format(item['url']), request.path_info): # # 新增一個點選樣式 # item['class'] = 'active' # break # return {"menu_list": menu_list} # 二級選單時 menu_dict = request.session.get(settings.PERMISSION_MENU_KEY) return {'menu_list': menu_dict.values()}
注意:為了不把資料寫死,便於維護,存在session中的許可權相關配置寫在setting中
# session中保留許可權key PERMISSION_SESSION_KEY = 'permissions' # 保留選單資訊key PERMISSION_MENU_KEY = 'menus' # 白名單 WHITE_LIST = [ r'^/login/$', r'^/reg/$', r'^/admin/.*', ]
在templates模板中應用選單
html
{% load rbac %} {# <!--應用inclusion_tag('rbac/menu.html')-->#} {% menu request %}