目標:生成一個獨立的組件,到哪都能用
一、先創建一個 項目,建一個app01和rbac的應用
二、表結構設計
1、先看配置文件合適不,給創建的rbac在配置文件里面設置一下
找到INSTALLED_APPS=['rbac']

配置靜態文件

2、設計表結構
models中創建類:五個類,七張表
角色表:
用戶表:
權限表:
權限組表:
菜單表:
角色表和權限表是多對多的關系(一個角色可以有多個權限,一個權限可以對應多個角色)
用戶表和角色表是多對多的關系(一個用戶可以有多個角色,一個角色有多個用戶)
所以有會多生成兩張關系表
一個菜單下面有多個組
一個組下面有多個菜單
一個菜單下面有多個權限
from django.db import models
# Create your models here.
class Role(models.Model):
title = models.CharField(max_length=32,verbose_name="角色")
permissions = models.ManyToManyField(to="Permission",verbose_name="擁有權限的角色",blank=True) #權限和角色是多對多的關系
def __str__(self):
return self.title
class Meta:
verbose_name_plural = "角色表"
class Permission(models.Model):
title = models.CharField(max_length=32,verbose_name="權限名")
url = models.CharField(max_length=32,verbose_name="帶正則的url")
codes = models.CharField(max_length=32,verbose_name="代碼")
group = models.ForeignKey(to="Group",verbose_name="所屬組",blank=True) #組和權限是一對多的關系,一個組有多個權限
menu_gp = models.ForeignKey(to='Permission',related_name='aaa',null=True,blank=True,verbose_name="組內菜單")
def __str__(self):
return self.title
class Meta:
verbose_name_plural = "權限表"
class UserInfo(models.Model):
name = models.CharField(max_length=32,verbose_name="姓名")
password = models.CharField(max_length=64,verbose_name="密碼")
email = models.CharField(max_length=32,verbose_name="郵箱")
roles = models.ManyToManyField(to="Role",blank=True) #用戶和角色是多對多的關系
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "用戶表"
class Group(models.Model):
title = models.CharField(max_length=32,verbose_name="組名稱")
menu = models.ForeignKey(to="Menu",verbose_name="組內菜單",blank=True) #一個組下有多個菜單
def __str__(self):
return self.title
class Meta:
verbose_name_plural = "權限組"
class Menu(models.Model):
caption = models.CharField(max_length=32,verbose_name="菜單")
def __str__(self):
return self.caption
class Meta:
verbose_name_plural = "菜單表"
具體分析為什么要多加個code列和權限組表呢?
1、我們一般是先看到的是列表頁面,在這個頁面上是否顯示添加,是否顯示編輯,是否顯示刪除,都是需要判斷的
有無添加權限,有無刪除權限,有無編輯權限,我們可以給每一個url一個代號
dict = {
1:{ 代號
/userinfo/ list
/userinfo/add/ add
/userinfo/del(\d+)/ del
/userinfo/edit(\d+)/ edit
}
}
不僅在列表頁面需要知道他有那些權限,在其他頁面也知道他有那些權限
所以上面的方案還是有點不好,那么我們采取下面的方案。將代號取出來放在一個列表里面
dict = {
1:{
"codes":["list","add","del","edit"]
urls:[
"/userinfo/",
"/userinfo/add"/,
"/userinfo/del(\d+)/ ",
"/userinfo/edit(\d+)/ ",
]
}
2:{
"codes":{"list","add","del","edit"}
urls:[
"/order",
"/order/add"/,
"/order/del(\d+)/ ",
"/order/edit(\d+)/ ",
]
}
}
把這個字典存到session中
當你訪問頁面的時候我就知道你有什么權限
一個url對應一個code
多個url對應一個組
注意:
關聯字段 null = True 數據庫用的時候可以為空
關聯字段 blank = True admin用的時候可以為空
當出現這個錯誤的時候

解決辦法
python manage.py migrate --fake 廢棄
三、通過django-admin錄入權限數據
- 先創建一個超級用戶 python3 manage.py createsuperuser - 用戶名 root - 密碼 zhy123456 - 在admin.py 中 from rbac import models admin.site.register(models.Permission) admin.site.register(models.Role) admin.site.register(models.UserInfo) 這樣的話上去的是英文的,如果你想讓中文顯示就在類中加一個類 class Meta: verbose_name_plural = "權限表" - 當你給關聯字段錄入數據的時候會有錯誤提示,那么在類中你的那個關聯字段在加一個屬性blank = True 可以為空 permissions = models.ManyToManyField(to="Permission",verbose_name="具有的所有權限", blank=True)
四、編寫登錄
1.編寫登錄
2.如果用戶驗證成功就設置session
3.先查出當前用戶的所有的權限
4.從這些權限中找到所有的url,吧這些url放到session中
這些都是在rbac里面的操作,如果我們做一些復雜的操作,可能會有好多的代碼
我們寫rbac的目的是做成一個公共的組件,為了讓別人省事
我們在創建一個server的文件夾,里面創建一個init_permission的py文件。
結構化數據:方便以后做操作。。。
dict = {
1:{
"codes":["list","add","del","edit"]
urls:[
"/userinfo/",
"/userinfo/add"/,
"/userinfo/del(\d+)/ ",
"/userinfo/edit(\d+)/ ",
]
}
2:{
"codes":{"list","add","del","edit"}
urls:[
"/order",
"/order/add"/,
"/order/del(\d+)/ ",
"/order/edit(\d+)/ ",
]
}
}
5.拿到用戶請求的url去session里面做驗證
獲取當前請求的url
獲取session中保存當前用戶的權限
然后開始驗證
如果匹配成功就有權訪問
如果匹配不成功就無權訪問
用re去匹配的時候,re.match(/userinfo/,/userinfo/add) #都能匹配到
那么要記得在匹配正則的時候加個起始符和終止符regex = "^{0}$".format(url)
def login(request):
.....
設置session
def index(request):
....
獲取session
def userinfo(request):
獲取session
這樣如果有好多個函數,就的重復好多代碼,我們可以用中間件來處理
中間件和裝飾器的區別:
中間件用來做批量處理
如果函數不多的話可以用加裝飾器的方法
五、中間件:獲取session,並且當用戶匹配成功的時候,先把code保存在request中,方便以后判斷
1、記得要配置白名單

2、必須繼承MiddlewareMixin這個類
class MiddlewareMixin(object):
def __init__(self, get_response=None):
self.get_response = get_response
super(MiddlewareMixin, self).__init__()
def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
if not response:
response = self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
六、設計權限管理-----問題:在訪問列表頁面時,是否需要判斷有無添加權限、有無刪除權限、有無編輯權限。
views.py
def userinfo(request):
# 方式一
# Page_permission = request.permission_code_list
# 方式二:實例化
page_permission = BasePagePermission(request.permission_code_list)
print("page_permission",request.permission_code_list)
data_list = [
{"id":1,"name":"xxx1"},
{"id":2,"name":"xxx2"},
{"id":3,"name":"xxx3"},
{"id":4,"name":"xxx4"},
{"id":5,"name":"xxx5"},
]
return render(request,"userinfo.html",{"data_list":data_list,"page_permission":page_permission})
在模板userinfo.html中:兩種使用方式
方式一:
<table>
{% if "add" in Page_permission %}
<a href="#">添加</a>
{% endif %}
{% for row in data_list %}
<tr>
<td>{{ row.id }}</td>
<td>{{ row.name }}</td>
{% if "edit" in Page_permission %}
<td><a href="#">編輯</a></td>
{% endif %}
{% if "del" in Page_permission %}
<td>{<a href="#">刪除</a></td>
{% endif %}
</tr>
{% endfor %}
</table>
如果不想像上面一樣每個都判斷,那么還有第二種方法,
方式二:
吧permission_code_list處理一下
在views中定義一個類
class BasePagePermission(object):
def __init__(self,code_list):
self.code_list = code_list
def has_add(self):
if "add" in self.code_list:
return True
def has_del(self):
if "del" in self.code_list:
return True
def has_edit(self):
if "edit" in self.code_list:
return True
實例化:page_permission = BasePagePermission(request.permission_code_list)
在模板中
<table>
{% if page_permission.has_add %}
<a href="#">添加</a>
{% endif %}
{% for row in data_list %}
<tr>
<td>{{ row.id }}</td>
<td>{{ row.name }}</td>
{% if page_permission.has_edit %}
<td><a href="#">編輯</a></td>
{% endif %}
{% if page_permission.has_del %}
<td>{<a href="#">刪除</a></td>
{% endif %}
</tr>
{% endfor %}
</table>
七、設計菜單管理-----問題:1、如何生成菜單
2、怎么讓這些菜單分級顯示並且如果當前訪問的url權限默認展開如果是組內菜單就加粗或者變紅
3、非菜單url,默認選中原菜單。(如果你是點擊用戶列表進來的,那么你看到頁面了,如果你點擊添加的時候,你的那個用戶列看不見了,這就不好了。所以要設計當你點擊添加按鈕的時候,那個用戶列表被默認選中)
菜單管理
菜單一
用戶管理
權限管理
菜單二
訂單管理
角色管理
分級做了菜單。這些菜單該顯示什么菜單?是當前用戶登錄之后從數據庫拿到這個用戶擁有的權限,然后把權限搞成菜單
在表里面設計了一個組內菜單(自關聯 ),當menu_gp_id為NULL就代表可以作為菜單

1、在初始化的時候,初始化權限信息,獲取權限信息並放置到session中
menu_list = []
for item in permission_list:
tpl = {
"id":item["permissions__id"],
"title":item["permissions__title"],
"url":item["permissions__url"],
"menu_gp_id":item["permissions__menu_gp_id"],
"menu_id":item["permissions__group__menu_id"],
"menu_title":item["permissions__group__menu__caption"]
}
menu_list.append(tpl)
request.session[settings.PERMISSION_MENU_KEY] = menu_list
因為是要在頁面上渲染,一般我們會在視圖函數的render里面加{"":變量}這樣渲染,
但是還有個更好用的方法:用自定義的標簽
具體操作:
1、找到app創建一個templatetags的文件夾
2、然后在里面隨便創建一個文件
3、導入form django.template import Library
register = Library()
方式一:
@register.simple_tag
def menu():
return "菜單" 這里返回啥頁面上就顯示啥
然后在母版里面導入mnue.html
{% load rbac %}
方式二:
@register.includsion_tag("xxx.html") #這里存放的是html文件,,,@register.includsion_tag("xxx.html") 自動會讀這個文件並且把返回值拿到在頁面上渲染
def menu():
return "菜單" 這里返回啥頁面上就顯示啥
“在母版中:{%menu_html request%} request是參數,記得要加上{% load rbac %}
4、注意:
如果有兩個文件夾同名,避免發生沖突:就再創建一個文件夾包起來
2、去Session中獲取菜單相關信息,匹配當前URL,生成菜單
①先把和菜單相關的所有字段取出來
menu_list = [
{'id': 1, 'title': '用戶列表', 'url': '/userinfo/', 'menu_gp_id': None, 'menu_id': 2, 'menu_title': '菜單二'},
{'id': 2, 'title': '添加用戶', 'url': '/userinfo/add/', 'menu_gp_id': 1, 'menu_id': 2, 'menu_title': '菜單二'},
{'id': 3, 'title': '刪除用戶', 'url': '/userinfo/del/(\\d+)/', 'menu_gp_id': 1, 'menu_id': 2, 'menu_title': '菜單二'},
{'id': 4, 'title': '編輯用戶', 'url': '/userinfo/edit/(\\d+)/', 'menu_gp_id': 1, 'menu_id': 2, 'menu_title': '菜單二'},
{'id': 5, 'title': '訂單列表','url': '/order/', 'menu_gp_id': None, 'menu_id': 1, 'menu_title': '菜單一'},
{'id': 6, 'title': '添加訂單', 'url': '/order/add/', 'menu_gp_id': 2, 'menu_id': 1, 'menu_title': '菜單一'},
{'id': 7, 'title': '刪除訂單', 'url': '/order/del/(\\d+)/', 'menu_gp_id': 2, 'menu_id': 1, 'menu_title': '菜單一'},
{'id': 8, 'title': '編輯訂單', 'url': '/order/edit/(\\d+)/', 'menu_gp_id': 2, 'menu_id': 1, 'menu_title': '菜單一'}
]
②然后循環列表找出可以作為菜單的權限
{
1: {'id': 1, 'title': '用戶列表', 'url': '/userinfo/', 'menu_gp_id': None, 'menu_id': 2, 'menu_title': '菜單二'},
5: {'id': 5, 'title': '訂單列表', 'url': '/order/', 'menu_gp_id': None, 'menu_id': 1, 'menu_title': '菜單一'}
}
③再次循環列表向上邊的字典中添加active
{
1: {'id': 1, 'title': '用戶列表', 'url': '/userinfo/', 'menu_gp_id': None, 'menu_id': 2, 'menu_title': '菜單二', 'active': True},
5: {'id': 5, 'title': '訂單列表', 'url': '/order/', 'menu_gp_id': None, 'menu_id': 1, 'menu_title': '菜單一'}
}
④結構化數據(吧上面得到的數據化成下面這樣格式的,方便以后使用)
{
1: {
'menu_id': 1,
'menu_title': '菜單一',
'active': None,
'children': [
{'title': '訂單列表', 'url': '/order/', 'active': None}
]
}
2: {
'menu_id': 2,
'menu_title': '菜單二',
'active': True,
'children': [
{'title': '用戶列表', 'url': '/userinfo/', 'active': True}
]
},
}
