权限分配思路
在一个页面分为三个面板(左中右三块),分别展示所有用户,所有角色,所有权限。
用户的展示:将所有用户展示,点击用户显示所有角色界面,为该用户分配角色其分配角色。
角色的展示:展示被点击用户所拥有的角色,选中角色对应的checkbox框来为该用户分配权限。
权限的展示:有三层,一级菜单->可做二级菜单的权限->不可做菜单的权限,可做三层循环来展示,为其构造用于循环展示的格式的数据
menu_permission_list = [
# 一级菜单
{
'id': 1,
'title': '业务管理',
'children' : [
# 二级菜单
{
'id': 11,
'title': '账单列表',
'children': [
# 不可做菜单的权限
{
'id': 3,
'title': '添加账单'
}
]
}
]
}
]
权限分配页面显示
路由
url(r'^distribute/permissions/', menu.distribute_permissions, name='distribute_permissions')
试图函数,构造权限信息展示的结构
def distribute_permissions(request):
all_users_list = models.UserInfo.objects.all()
all_roles_list = models.Role.objects.all()
# 所有的一级菜单
all_menu_list = models.Menu.objects.values('id', 'title')
# 所有二级菜单
all_second_menu_list = models.Permission.objects.filter(menu__isnull=False).values('id', 'title', 'menu_id')
# 所有三级菜单
all_permission_list = models.Permission.objects.filter(menu__isnull=True).values('id', 'title', 'pid_id')
# 使用字典来同步更新列表中的值,key对应的value和列表中的元素指向的是同一块内存地址。
all_menu_dict = {}
all_second_menu_dict = {}
for item in all_menu_list:
item['children'] = []
all_menu_dict[item['id']] = item
# 二级菜单挂靠到一级菜单下
for row in all_second_menu_list:
row['children'] = []
menu_id = row.get('menu_id')
all_second_menu_dict[row['id']] = row
all_menu_dict[menu_id]['children'].append(row)
# 不能做菜单的权限挂靠到二级菜单
for row in all_permission_list:
pid_id = row.get('pid_id')
if pid_id:
all_second_menu_dict[pid_id]['children'].append(row)
return render(
request,
'rbac/distribute_permissions.html',
{
'user_list': all_users_list,
'role_list': all_roles_list,
'menu_list': all_menu_list
},
)
模板distribute_permissions.html
{% extends 'layout.html' %}
{% load rbac %}
{% block css %}
<style>
tr.active{
border-left: 3px solid #fdc00f
}
.permission-area tr.root{
background-color: #f1f7fd;
}
.permission-area tr.root td i{
margin: 3px;
}
.permission-area .node{
}
.permission-area .node input[type='checkbox']{
margin: 0 5px;
}
.permission-area .node .parent{
padding: 5px 0;
}
.permission-area .node label{
font-weight: normal;
margi-bottom: 0
font-size: 12px;
}
.permission-area .node .children{
padding: 0 0 0 25px;
}
.permission-area .node .children .child{
display: inline-block;
margin: 2px 5px;
}
.select-help{
float: right;
}
.select-help .label{
font-weight: normal;
cursor: pointer;
}
.select-help .check-all{
float: right;
display: inline-block;
margin-right: 8px;
}
</style>
{% endblock %}
{% block content %}
<div class="luffy-container">
<div class="col-md-3">
<div class="panel panel-info">
<!-- Default panel contents -->
<div class="panel-heading">
<i class="fa fa-book" aria-hidden="true"></i>用户信息
</div>
<!-- Table -->
<ul>
{% for row in user_list %}
<li><a href="?uid={{ row.id }}">{{ row.name }}</a></li>
{% endfor %}
</ul>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-info">
<!-- Default panel contents -->
<div class="panel-heading">
<i class="fa fa-book" aria-hidden="true"></i>角色信息
</div>
<div class="panel-body">
提示:选中用户才能为其分配角色
</div>
<table class="table">
<thead>
<tr>
<th>角色</th>
<th>选项</th>
</tr>
</thead>
<tbody>
{% for row in role_list %}
<tr>
<td>{{ row.title }}</td>
<td><input type="checkbox" name="roles" id="" value="{{ row.id }}"></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-5 permission-area">
<div class="panel panel-info">
<!-- Default panel contents -->
<div class="panel-heading">
<i class="fa fa-book" aria-hidden="true"></i>权限分配
</div>
<div class="panel-body">
提示:选中角色才能为其分配权限
</div>
<table class="table">
<tbody>
{% for item in menu_list %}
<tr class="root">
<td>
<i class="fa fa-caret-down" aria-hidden=""></i>
{{ item.title }}
<div class="select-help">
<div class="check-all">
<label for="check-all_{{ item.id }}">全选</label>
<input type="checkbox" name="" id="check-all_{{ item.id }}">
</div>
</div>
</td>
</tr>
<tr class="node">
<td>
{% for node in item.children %}
<div class="parent">
<input type="checkbox" name="permissions" id="permission_{{ node.id }}"
value="{{ node.id }}">
<label for="permission_{{ node.id }}">{{ node.title }} (菜单)</label>
</div>
<div class="children">
{% for child in node.children %}
<div class="child">
<input type="checkbox" name="permissions" id="permission_{{ child.id }}"
value="{{ child.id }}">
<label for="permission_{{ child.id }}">{{ child.title }}</label>
</div>
{% endfor %}
</div>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
效果
点击选中的用户展示该用户拥有的角色
根据用户id拿到该用户下的所有角色,将所有角色的id已{'id值':None}
的形式存储。
# 筛选出当前用户拥有的角色
if user_id:
user_has_roles = user_obj.roles.all()
else:
user_has_roles = []
user_has_roles_dict = {item.id: None for item in user_has_roles}
在前端展示checkbox时进行判断,如果其角色id在user_has_roles_dict中,说明用户已拥有该角色,将其设置为checked,显示为选中。
<tbody>
{% for row in role_list %}
<tr>
<td>{{ row.title }}</td>
<td><input type="checkbox" name="roles" value="{{ row.id }}" {% if row.id in user_has_roles_dict %}checked{% endif %}></td>
</tr>
{% endfor %}
</tbody>
点击选中的用户展示该用户拥有的所有权限
根据用户对象,拿到该用户拥有的所有权限,将权限的id以{‘权限id值’:None}的形式存储,注意跨表,用户表->角色表->拿到角色下的所有权限,角色和权限是多对多关系,获取的到权限可能重复,要做去重。
# 筛选出当前用户拥有的权限
if user_id:
user_has_permissions = user_obj.roles.filter(permissions__id__isnull=False).values('id', 'permissions').distinct()
else:
user_has_permissions = []
user_has_permissions_dict = {item['permissions']: None for item in user_has_permissions}
在前端展示checkbox时进行判断,如果其角色id在user_has_permissions_dict 中,说明用户已拥有该角色,将其设置为checked,显示为选中。
<tr class="node">
<td>
{% for node in item.children %}
<div class="parent">
<input type="checkbox" name="permissions" id="permission_{{ node.id }}"
value="{{ node.id }}"{% if node.id in user_has_permissions_dict %}checked{% endif %}>
<label for="permission_{{ node.id }}">{{ node.title }} (菜单)</label>
</div>
<div class="children">
{% for child in node.children %}
<div class="child">
<input type="checkbox" name="permissions" id="permission_{{ child.id }}"
value="{{ child.id }}" {% if child.id in user_has_permissions_dict %}checked{% endif %}>
<label for="permission_{{ child.id }}">{{ child.title }}</label>
</div>
{% endfor %}
</div>
{% endfor %}
</td>
</tr>
效果:
点击角色显示角色下的已分配权限信息
点击角色显示该角色选中的样式:
.role-area tr td a{
display:block;
}
.role-area tr.active{
background-color:#f1f7fd;
border-left: 3px solid #fdc00f;
}
有两种情况进行处理:
- 在点击了用户后,再点击角色
- 直接点击角色
给用户分配权限,是通过角色间接的分配权限,直接点击角色,显示角色的权限分配
点击用户后,再点击角色,会显示该角色下的权限,给该角色权限进行分配,但是用户分配角色的功能应该不受影响,依旧可以分配角色给用户。点击角色信息的保存,给用户分配角色;点击权限分配的保存,给角色分配权限。
代码实现:
视图函数
def distribute_permissions(request):
user_id = request.GET.get('uid')
role_id = request.GET.get('rid')
user_obj = models.UserInfo.objects.filter(pk=user_id).first()
role_obj = models.Role.objects.filter(pk=role_id).first()
if not user_obj:
user_id = None
if not role_obj:
role_id = None
# 筛选出当前用户拥有的角色
if user_id:
user_has_roles = user_obj.roles.all()
else:
user_has_roles = []
user_has_roles_dict = {item.id: None for item in user_has_roles}
# 筛选出当前用户拥有的权限
# 如果选中了角色,优先显示选中角色所有的权限;如果没有选中角色,才显示用户所拥有的权限
if role_obj: # 选择了角色
user_has_permissions = role_obj.permissions.values('id')
user_has_permissions_dict = {item['id']: None for item in user_has_permissions}
elif user_obj: # 选择了用户,没有选择角色
user_has_permissions = user_obj.roles.filter(permissions__id__isnull=False).values('id',
'permissions').distinct()
user_has_permissions_dict = {item['permissions']: None for item in user_has_permissions}
else:
user_has_permissions_dict = {}
all_users_list = models.UserInfo.objects.all()
all_roles_list = models.Role.objects.all()
# 所有的一级菜单
all_menu_list = models.Menu.objects.values('id', 'title')
# 所有二级菜单
all_second_menu_list = models.Permission.objects.filter(menu__isnull=False).values('id', 'title', 'menu_id')
# 所有三级菜单
all_permission_list = models.Permission.objects.filter(menu__isnull=True).values('id', 'title', 'pid_id')
# 使用字典来同步更新列表中的值,key对应的value和列表中的元素指向的是同一块内存地址。
all_menu_dict = {}
all_second_menu_dict = {}
for item in all_menu_list:
item['children'] = []
all_menu_dict[item['id']] = item
# 二级菜单挂靠到一级菜单下
for row in all_second_menu_list:
row['children'] = []
menu_id = row.get('menu_id')
all_second_menu_dict[row['id']] = row
all_menu_dict[menu_id]['children'].append(row)
# 不能做菜单的权限挂靠到二级菜单
for row in all_permission_list:
pid_id = row.get('pid_id')
if pid_id:
all_second_menu_dict[pid_id]['children'].append(row)
return render(
request,
'rbac/distribute_permissions.html',
{
'user_list': all_users_list,
'role_list': all_roles_list,
'menu_list': all_menu_list,
'user_id': user_id,
'role_id': role_id,
'user_has_roles_dict': user_has_roles_dict,
'user_has_permissions_dict': user_has_permissions_dict,
},
)
模板
{% extends 'layout.html' %}
{% load rbac %}
{% block css %}
<style>
.user-area ul{
padding-left: 20px;
}
.user-area li{
cursor: pointer;
padding: 2px 0;
}
.user-area li a{
display:block;
}
.user-area li.active{
font-weight: bold;
color:red;
}
.user-area li.active a{
color:red
}
.role-area tr td a{
display:block;
}
.role-area tr.active{
background-color:#f1f7fd;
border-left: 3px solid #fdc00f;
}
.permission-area tr.root{
background-color: #f1f7fd;
}
.permission-area tr.root td i{
margin: 3px;
}
.permission-area .node{
}
.permission-area .node input[type='checkbox']{
margin: 0 5px;
}
.permission-area .node .parent{
padding: 5px 0;
}
.permission-area .node label{
font-weight: normal;
margi-bottom: 0
font-size: 12px;
}
.permission-area .node .children{
padding: 0 0 0 25px;
}
.permission-area .node .children .child{
display: inline-block;
margin: 2px 5px;
}
.select-help{
float: right;
}
.select-help .label{
font-weight: normal;
cursor: pointer;
}
.select-help .check-all{
float: right;
display: inline-block;
margin-right: 8px;
}
</style>
{% endblock %}
{% block content %}
<div class="luffy-container">
<div class="col-md-3 user-area">
<div class="panel panel-info">
<!-- Default panel contents -->
<div class="panel-heading">
<i class="fa fa-book" aria-hidden="true"></i>用户信息
</div>
<div class="panel-body">
<ul>
{% for row in user_list %}
<li class="{% if user_id == row.id|safe %} active {% endif %}"><a href="?uid={{ row.id }}">{{ row.name }}</a></li>
{% endfor %}
</ul>
</div>
</div>
</div>
<div class="col-md-4 role-area">
<div class="panel panel-info">
<!-- Default panel contents -->
<div class="panel-heading">
<i class="fa fa-book" aria-hidden="true"></i>角色信息
{% if user_id %}
<a href="#" class="right btn btn-primary btx-xs"
style="margin: -3px; padding: 2px 8px;">
<i class="fa fa-save" aria-hidden="true"></i>
保存
</a>
{% endif %}
</div>
<div class="panel-body">
提示:选中用户才能为其分配角色
</div>
<table class="table">
<thead>
<tr>
<th>角色</th>
<th>选项</th>
</tr>
</thead>
<tbody>
{% for row in role_list %}
<tr class="{% if row.id|safe == role_id %}active{% endif %}">
<td>
{% if user_id %}
<a href="?uid={{ user_id }}&rid={{ row.id }}">{{ row.title }}</a>
{% else %}
<a href="?rid={{ row.id }}">{{ row.title }}</a>
{% endif %}
</td>
<td><input type="checkbox" name="roles" value="{{ row.id }}" {% if row.id in user_has_roles_dict %}checked{% endif %}></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-5 permission-area">
<div class="panel panel-info">
<!-- Default panel contents -->
<div class="panel-heading">
<i class="fa fa-book" aria-hidden="true"></i>权限分配
{% if role_id %}
<a href="#" class="right btn btn-primary btx-xs"
style="margin: -3px; padding: 2px 8px;">
<i class="fa fa-save" aria-hidden="true"></i>
保存
</a>
{% endif %}
</div>
<div class="panel-body">
提示:选中角色才能为其分配权限
</div>
<table class="table">
<tbody>
{% for item in menu_list %}
<tr class="root">
<td>
<i class="fa fa-caret-down" aria-hidden=""></i>
{{ item.title }}
<div class="select-help">
<div class="check-all">
<label for="check-all_{{ item.id }}">全选</label>
<input type="checkbox" name="" id="check-all_{{ item.id }}">
</div>
</div>
</td>
</tr>
<tr class="node">
<td>
{% for node in item.children %}
<div class="parent">
<input type="checkbox" name="permissions" id="permission_{{ node.id }}"
value="{{ node.id }}"{% if node.id in user_has_permissions_dict %}checked{% endif %}>
<label for="permission_{{ node.id }}">{{ node.title }} (菜单)</label>
</div>
<div class="children">
{% for child in node.children %}
<div class="child">
<input type="checkbox" name="permissions" id="permission_{{ child.id }}"
value="{{ child.id }}" {% if child.id in user_has_permissions_dict %}checked{% endif %}>
<label for="permission_{{ child.id }}">{{ child.title }}</label>
</div>
{% endfor %}
</div>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
权限分配页面保存按钮
有两个保存按钮,一个是角色分配的保存按钮,一个是权限分配的保存按钮。也就是说点击会有两个form表单提交到同一url到后台,后台需要区分不同的表单进行处理,区分的方式是在各自的form下写一个intput标签<input type="text" name="type" value="permission" hidden>
<input type="text" name="type" value="permission" hidden>
后台获取提交的input的标签内容来区分不同的表单
if request.method == "POST" and request.POST.get('type') == "role":
print('角色')
if request.method == "POST" and request.POST.get('type') == "permission":
print('权限')
后端进行保存:
if request.method == "POST" and request.POST.get('type') == "role":
role_id_list = request.POST.getlist('roles')
if not user_obj:
return HttpResponse('请选择用户,再分配角色')
user_obj.roles.set(role_id_list)
if request.method == "POST" and request.POST.get('type') == "permission":
permission_id_list = request.POST.getlist('permissions')
if not role_obj:
return HttpResponse('请选择角色,再分配权限')
role_obj.permissions.set(permission_id_list)
权限分配之全选
使用js来实现,再点击权限时,给每个input checkbox标签加上checked就可以
{% block js %}
<script type="text/javascript">
$(function(){
$('.check-all input:checkbox').change(function(){
$(this).parents('.root').next().find(':checkbox').prop('checked', $(this).prop('checked'))
})
})
</script>
{% endblock %}