Django之权限管理
一, 引入
1.为什么要有权限?
2.为什么要开发权限的组件?
3.在web开发中,什么是权限?
4.表结构的设计
权限表
ID | URL |
---|---|
1 | /user_list/ |
2 | /customer_list/ |
用户表
ID | USER_NAME |
---|---|
1 | root |
2 | root 2 |
角色/用户组表
ID | 组 |
---|---|
1 | 销售 |
2 | 开发 |
用户与角色的关系表
ID | USER_ID | 角色ID |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 1 |
4 | 2 | 2 |
角色与权限的关系表
ID | 角色ID | 权限ID |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 1 |
4 | 2 | 2 |
models:
from django.db import models
# 权限表
class Permission(models.Model):
url = models.CharField(max_length=108, verbose_name='权限')
# 角色表
class Role(models.Model):
name = models.CharField(max_length=108, verbose_name='角色')
permissions = models.ManyToManyField('Permission', verbose_name='角色所拥有的权限', related_name='roles')
# 用户表
class UserInfo(models.Model):
username = models.CharField(max_length=32, verbose_name='用户名')
password = models.CharField(max_length=32, verbose_name='密码')
roles = models.ManyToManyField('Role', verbose_name='用户所拥有的角色', related_name='users')
基本流程:
二, admin的展示
from django.contrib import admin
from rbac import models
# 配置类
class PermissionAdmin(admin.ModelAdmin):
# 展示
list_display = ['title', 'url']
# 可编辑
list_editable = ['url']
admin.site.register(models.Role)
admin.site.register(models.Permission, admin_class=PermissionAdmin)
admin.site.register(models.UserInfo)
三, 记录登录状态与权限
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect, reverse
from django.conf import settings
import re
def login(request):
error = ''
if request.method == 'POST':
# 获取用户名和密码
username = request.POST.get('username')
password = request.POST.get('password')
# 数据库校验用户名密码是否正确
user_obj = models.UserInfo.objects.filter(username=username, password=password).first()
if not user_obj:
# 校验失败,返回登录页面
error = '用户名或密码错误'
else:
# 登录成功
# 查询当前用户的权限信息
# permission = models.Permission.objects.filter(roles__users=user_obj).values('url').distinct()
permission = user_obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url').distinct()
# 保存权限信息
request.session['permission'] = list(permission)
request.session['is_login'] = 1
# 重定向去首页
return redirect('index')
return render(request, 'login.html', {'error': error})
四, 中间件校验权限
class Rbac(MiddlewareMixin):
def process_request(self, request):
# 获取当前访问的地址
url = request.path_info
# 白名单
for i in settings.WHITE_LIST:
if re.match(i, url):
return
# 登陆状态的校验
is_login = request.session.get('is_login')
if not is_login:
return redirect('login]')
# 免认证校验
for i in settings.PASS_AUTH_LIST:
if re.match(i, url):
return
# 获取权限信息
permissions = request.session.get('permission')
# 权限的校验
for i in permissions:
if re.match(r'^{}$'.format(i['permissions__url']), url):
return
# 拒绝请求
return HttpResponse('没有访问权限,请联系管理员')
# settings.py中
# rbac 白名单
WHITE_LIST = [
r'^/admin/',
r'^/login/$',
r'^/register/$',
]
# rabc 免认证
PASS_AUTH_LIST =[
r'^/index/$'
]
五, 动态生成一级菜单
首先规范化RBAC:
在settings.py中配置
PERMISSION_SESSION_KEY session中记录权限的key
MENU_SESSION_KEY session中记录菜单信息的key
LOGIN_SESSION_KEY session中记录登录状态的key
将相关template,static,服务等整理到rbac(app)内
model的更改:
# 权限表
class Permission(models.Model):
url = models.CharField(max_length=108, verbose_name='权限')
title = models.CharField(max_length=108, verbose_name='标题')
is_menu = models.BooleanField(default=False, choices=((True, '是'), (False, '否')), verbose_name='是否目录')
icon = models.CharField(max_length=64, verbose_name='图标', null=True, blank=True)
def __str__(self):
return self.title
session记录的更改:
# 查询当前用户的权限信息
# permission = models.Permission.objects.filter(roles__users=user_obj).values('url').distinct()
permission = user_obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
'permissions__title',
'permissions__is_menu',
'permissions__icon',
).distinct()
# 权限的列表
permission_list = []
# 菜单的列表
menu_list = []
for i in permission:
permission_list.append({
'url': i['permissions__url']
})
if i.get('permissions__is_menu'):
menu_list.append({
'title': i['permissions__title'],
'icon': i['permissions__icon'],
'url': i['permissions__url']
})
# 保存权限信息
request.session[settings.PERMISSION_SESSION_KEY] = permission_list
request.session[settings.LOGIN_SESSION_KEY] = True
# 保存菜单信息
request.session[settings.MENU_SESSION_KEY] = menu_list
自定义inclusion_tag:
// 插件 menu.html
<div class="static-menu">
{% for menu in menu_list %}
<a href="{{ menu.url }}" class="{{ menu.class }}">
<span class="icon-wrap"><i class="fa {{ menu.icon }}"></i></span> {{ menu.title }}</a>
{% endfor %}
</div>
from django import template
from django.conf import settings
register = template.Library()
@register.inclusion_tag('rbac/menu.html')
def menu(request):
menu_list = request.session.get(settings.MENU_SESSION_KEY)
url = request.path_info
for menu in menu_list:
if menu.get('url') == url:
menu['class'] = 'active'
return {'menu_list': request.session.get(settings.MENU_SESSION_KEY)}
六, 动态生成二级菜单
model的更改:
# 一级菜单表
class Menu(models.Model):
title = models.CharField(max_length=108, verbose_name='一级菜单标题')
icon = models.CharField(max_length=64, verbose_name='图标', null=True, blank=True)
def __str__(self):
return self.title
# 权限表(二级菜单)
class Permission(models.Model):
'''
menu_id: 有menu_id表示当前的权限是二级菜单
没有menu_id表示当前的权限是普通权限
'''
url = models.CharField(max_length=108, verbose_name='权限')
title = models.CharField(max_length=108, verbose_name='二级菜单标题')
menu = models.ForeignKey('Menu', on_delete=models.CASCADE, verbose_name='一级菜单', null=True, blank=True)
def __str__(self):
return self.title
session记录的更改:
# 查询当前用户的权限信息
# permission = models.Permission.objects.filter(roles__users=user_obj).values('url').distinct()
permission = user_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 i in permission:
permission_list.append({'url': i.get('permissions__url')})
menu_id = i.get('permissions__menu_id')
if menu_id:
menu_dict.setdefault(menu_id, {
'title': i['permissions__menu__title'],
'icon': i['permissions__menu__icon'],
'children': []
})
menu_dict[menu_id]['children'].append(
{
'title': i['permissions__title'],
'url': i['permissions__url']
}
)
# 保存权限信息
request.session[settings.PERMISSION_SESSION_KEY] = permission_list
request.session[settings.LOGIN_SESSION_KEY] = True
# 保存菜单信息
request.session[settings.MENU_SESSION_KEY] = menu_dict
自定义inclusion_tag:
// 插件中 menu.html
<div class="multi-menu">
{% for menu_first in menu_list %}
<div class="item">
<div class="title">
<i class="fa {{ menu_first.icon }}"></i> {{ menu_first.title }}
</div>
<div class="body">
{% for menu_second in menu_first.children %}
<a class="{{ menu_second.class }}" href="{{ menu_second.url }}">{{ menu_second.title }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
# inclusion_tag
@register.inclusion_tag('rbac/menu.html')
def menu(request):
menu_list = request.session.get(settings.MENU_SESSION_KEY).values()
url = request.path_info
for menu in menu_list:
for i in menu.get('children'):
if i.get('url') == url:
i['class'] = 'active'
return {'menu_list': menu_list}
七, 面包屑导航
model的更改:
# 一级菜单表
class Menu(models.Model):
title = models.CharField(max_length=108, verbose_name='一级菜单标题')
icon = models.CharField(max_length=64, verbose_name='图标', null=True, blank=True)
weight = models.IntegerField(default=1, verbose_name='权重') # 用于一级菜单排序
def __str__(self):
return self.title
# 权限表(二级菜单)
class Permission(models.Model):
url = models.CharField(max_length=108, verbose_name='权限')
title = models.CharField(max_length=108, verbose_name='二级菜单标题')
menu = models.ForeignKey('Menu', on_delete=models.CASCADE, verbose_name='一级菜单', null=True, blank=True)
parent = models.ForeignKey('self', on_delete=models.DO_NOTHING, verbose_name='父权限', null=True, blank=True) # 用于区分二级权限和三级权限,生成面包屑导航
def __str__(self):
return self.title
session记录的更改:
# 查询当前用户的权限信息
# permission = models.Permission.objects.filter(roles__users=user_obj).values('url').distinct()
permission = user_obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
'permissions__title',
'permissions__id',
'permissions__parent_id',
'permissions__menu__title',
'permissions__menu__icon',
'permissions__menu__weight',
'permissions__menu_id',
).distinct()
# 权限的字典
permission_dict = {}
# 菜单的字典
menu_dict = {}
for i in permission:
permission_dict[i.get('permissions__id')] = {
'url': i.get('permissions__url'),
'title': i.get('permissions__title'),
'id': i.get('permissions__id'),
'parent_id': i.get('permissions__parent_id')
}
menu_id = i.get('permissions__menu_id')
if menu_id:
menu_dict.setdefault(menu_id, {
'title': i['permissions__menu__title'],
'icon': i['permissions__menu__icon'],
'weight': i['permissions__menu__weight'],
'children': []
})
menu_dict[menu_id]['children'].append(
{
'title': i['permissions__title'],
'url': i['permissions__url'],
'id': i['permissions__id'],
}
)
# 保存权限信息
request.session[settings.PERMISSION_SESSION_KEY] = permission_dict
request.session[settings.LOGIN_SESSION_KEY] = True
# 保存菜单信息
request.session[settings.MENU_SESSION_KEY] = menu_dict
# 此时session记录信息数据格式
# 注意: 存入session中时经过json序列化,字典的key中的数字会转换为字符串
# 权限信息字典
{
1: {
'url': '/customer/list/',
'title': '客户列表',
'id': 1,
'parent_id': None
},
2: {
'url': '/customer/add/',
'title': '添加客户',
'id': 2,
'parent_id': 1
},
3: {
'url': '/customer/edit/(?P<cid>\\d+)/',
'title': '编辑客户',
'id': 3,
'parent_id': 1
},
4: {
'url': '/customer/del/(?P<cid>\\d+)/',
'title': '删除客户',
'id': 4,
'parent_id': 1
},
5: {
'url': '/payment/list/',
'title': '缴费列表',
'id': 5,
'parent_id': None
},
6: {
'url': '/payment/add/',
'title': '添加缴费',
'id': 6,
'parent_id': 5
},
7: {
'url': '/payment/edit/(?P<pid>\\d+)/',
'title': '编辑缴费',
'id': 7,
'parent_id': 5
},
8: {
'url': '/payment/del/(?P<pid>\\d+)/',
'title': '删除缴费',
'id': 8,
'parent_id': 5
}
}
# 菜单信息字典
{
1: {
'title': '客户管理',
'icon': 'fa-address-book',
'weight': 10,
'children': [{
'title': '客户列表',
'url': '/customer/list/',
'id': 1
}]
},
2: {
'title': '财务管理',
'icon': 'fa-money',
'weight': 1,
'children': [{
'title': '缴费列表',
'url': '/payment/list/',
'id': 5
}]
}
}
中间件的更改:
class Rbac(MiddlewareMixin):
def process_request(self, request):
# 获取当前访问的地址
url = request.path_info
# 初始化current_menu_id
request.current_menu_id = None
# 初始化导航列表
request.breadcrumb_list = [
{'title': '首页', 'url': '/index/'}
]
# 白名单
for i in settings.WHITE_LIST:
if re.match(i, url):
return
# 登陆状态的校验
is_login = request.session.get(settings.LOGIN_SESSION_KEY)
if not is_login:
return redirect('login')
# 免认证校验
for i in settings.PASS_AUTH_LIST:
if re.match(i, url):
return
# 获取权限信息
permissions = request.session.get(settings.PERMISSION_SESSION_KEY)
# 权限的校验
for i in permissions.values():
# i 权限 父权限 子权限
if re.match(fr'^{i["url"]}$', url):
menu_id = i.get('parent_id')
if not menu_id:
# 当前访问的权限是父权限(二级菜单)
menu_id = i.get('id')
request.breadcrumb_list.append({
'title': i['title'],
'url': i['url']
})
else:
# 当前访问的权限是子权限
parent_permission = permissions[str(menu_id)]
request.breadcrumb_list.append({
'title': parent_permission['title'],
'url': parent_permission['url'],
})
request.breadcrumb_list.append({
'title': i['title'],
'url': i['url'],
})
request.current_menu_id = menu_id
return
# 拒绝请求
return render(request, 'rbac/403.html')
自定义inclusion_tag:
@register.inclusion_tag('rbac/menu.html')
def menu(request):
menu_list = request.session.get(settings.MENU_SESSION_KEY).values()
od_menu_list = sorted(menu_list, key=lambda x: x['weight'], reverse=True)
for menu in od_menu_list:
menu['class'] = 'hidden'
for i in menu.get('children'):
if i.get('id') == request.current_menu_id:
i['class'] = 'active'
menu['class'] = ''
break
return {'menu_list': od_menu_list}
// 插件 menu.html
<div class="multi-menu">
{% for menu_first in menu_list %}
<div class="item">
<div class="title">
<i class="fa {{ menu_first.icon }}"></i> {{ menu_first.title }}
</div>
<div class="body {{ menu_first.class }}">
{% for menu_second in menu_first.children %}
<a class="{{ menu_second.class }}" href="{{ menu_second.url }}">{{ menu_second.title }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
--------------------------------------------------------------
// breadcrumb.html
<ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;">
{% for breadcrumb in breadcrumb_list %}
{% if forloop.last %}
<li class="active">{{ breadcrumb.title }}</li>
{% else %}
<li><a href="{{ breadcrumb.url }}">{{ breadcrumb.title }}</a></li>
{% endif %}
{% endfor %}
</ol>
八, 权限控制到按钮级别
model的更改:
# 权限表(二级菜单)
class Permission(models.Model):
'''
name: 权限控制到按钮级别,判断是否有这个权限
'''
url = models.CharField(max_length=108, verbose_name='权限')
name = models.CharField(max_length=108, verbose_name='url别名', unique=True)
title = models.CharField(max_length=108, verbose_name='二级菜单标题')
menu = models.ForeignKey('Menu', on_delete=models.CASCADE, verbose_name='一级菜单', null=True, blank=True)
parent = models.ForeignKey('self', on_delete=models.DO_NOTHING, verbose_name='父权限', null=True, blank=True)
def __str__(self):
return self.title
session记录的更改:
# 查询当前用户的权限信息
# permission = models.Permission.objects.filter(roles__users=user_obj).values('url').distinct()
permission = user_obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
'permissions__title',
'permissions__id',
'permissions__name',
'permissions__parent_id',
'permissions__parent__name',
'permissions__menu__title',
'permissions__menu__icon',
'permissions__menu__weight',
'permissions__menu_id',
).distinct()
# 权限的字典
permission_dict = {}
# 菜单的字典
menu_dict = {}
for i in permission:
permission_dict[i.get('permissions__name')] = {
'url': i.get('permissions__url'),
'title': i.get('permissions__title'),
'id': i.get('permissions__id'),
'parent_id': i.get('permissions__parent_id'),
'parent_name': i.get('permissions__parent__name'),
}
menu_id = i.get('permissions__menu_id')
if menu_id:
menu_dict.setdefault(menu_id, {
'title': i['permissions__menu__title'],
'icon': i['permissions__menu__icon'],
'weight': i['permissions__menu__weight'],
'children': []
})
menu_dict[menu_id]['children'].append(
{
'title': i['permissions__title'],
'url': i['permissions__url'],
'id': i['permissions__id'],
}
)
# 保存权限信息
request.session[settings.PERMISSION_SESSION_KEY] = permission_dict
request.session[settings.LOGIN_SESSION_KEY] = True
# 保存菜单信息
request.session[settings.MENU_SESSION_KEY] = menu_dict
中间件:
# 获取权限信息
permissions = request.session.get(settings.PERMISSION_SESSION_KEY)
# 权限的校验
for i in permissions.values():
# i权限 父权限 子权限
if re.match(fr'^{i["url"]}$', url):
menu_id = i.get('parent_id')
if not menu_id:
# 当前访问的权限是父权限(二级菜单)
menu_id = i.get('id')
request.breadcrumb_list.append({
'title': i['title'],
'url': i['url']
})
else:
# 当前访问的权限是子权限
parent_name = i.get('parent_name')
parent_permission = permissions[parent_name]
request.breadcrumb_list.append({
'title': parent_permission['title'],
'url': parent_permission['url'],
})
request.breadcrumb_list.append({
'title': i['title'],
'url': i['url'],
})
request.current_menu_id = menu_id
return
# 拒绝请求
return render(request, 'rbac/403.html')
自定义tags:
@register.inclusion_tag('rbac/menu.html')
def menu(request):
menu_list = request.session.get(settings.MENU_SESSION_KEY).values()
od_menu_list = sorted(menu_list, key=lambda x: x['weight'], reverse=True)
for menu in od_menu_list:
menu['class'] = 'hidden'
for i in menu.get('children'):
if i.get('id') == request.current_menu_id:
i['class'] = 'active'
menu['class'] = ''
break
return {'menu_list': od_menu_list}
@register.inclusion_tag('rbac/breadcrumb.html')
def breadcrumb(request):
return {
'breadcrumb_list': request.breadcrumb_list
}
@register.filter
def has_permission(request, name):
permission = request.session.get(settings.PERMISSION_SESSION_KEY)
if name in permission:
return True
模板中使用:
{% if request|has_permission:'customer_add' %}
<a class="btn btn-default" href="{% url 'customer_add' %}">
<i class="fa fa-plus-square" aria-hidden="true"></i> 添加客户
</a>
{% endif %}
九, 权限管理
# routes.py中
from django.conf import settings
from django.utils.module_loading import import_string
from django.urls import RegexURLResolver, RegexURLPattern
from collections import OrderedDict
def recursion_urls(pre_namespace, pre_url, urlpatterns, url_ordered_dict):
for item in urlpatterns:
if isinstance(item, RegexURLResolver):
if pre_namespace:
if item.namespace:
namespace = "%s:%s" % (pre_namespace, item.namespace,)
else:
namespace = pre_namespace
else:
if item.namespace:
namespace = item.namespace
else:
namespace = None
" None /^ "
recursion_urls(namespace, pre_url + item.regex.pattern, item.url_patterns, url_ordered_dict)
else:
if pre_namespace:
name = "%s:%s" % (pre_namespace, item.name,)
else:
name = item.name
if not item.name:
raise Exception('URL路由中必须设置name属性')
"""
/^^login/$ /login/
"""
url = pre_url + item._regex
url_ordered_dict[name] = {'name': name, 'url': url.replace('^', '').replace('$', '')}
def get_all_url_dict(ignore_namespace_list=None):
"""
获取路由中
:return:
"""
ignore_list = ignore_namespace_list or []
url_ordered_dict = OrderedDict()
md = import_string(settings.ROOT_URLCONF) # 根据路径的字符串 导入模块
urlpatterns = []
for item in md.urlpatterns:
if isinstance(item, RegexURLResolver) and item.namespace in ignore_list:
continue
urlpatterns.append(item)
recursion_urls(None, "/", urlpatterns, url_ordered_dict)
return url_ordered_dict
# views.py
from django.shortcuts import render, redirect, reverse, HttpResponse
from django.views import View
from rbac import models
from django.db.models import Q
from rbac.forms import RoleForm
from rbac.forms import MenuForm
from rbac.forms import PermissionForm
from rbac.forms import MultiPermissionForm
from django.forms import modelformset_factory, formset_factory
from rbac.service.routes import get_all_url_dict
# 展示角色
class RoleList(View):
def get(self, request):
all_role = models.Role.objects.all()
return render(request, 'rbac/role_list.html', {
'all_role': all_role
})
# 新增编辑角色
class RoleChange(View):
def get(self, request, role_id=None, *args, **kwargs):
role_obj = models.Role.objects.filter(pk=role_id).first()
form_obj = RoleForm(instance=role_obj)
return render(request, 'rbac/forms.html', {
'form_obj': form_obj,
})
def post(self, request, role_id=None, *args, **kwargs):
role_obj = models.Role.objects.filter(pk=role_id).first()
form_obj = RoleForm(instance=role_obj, data=request.POST)
if form_obj.is_valid():
form_obj.save()
return redirect('rbac:role_list')
return render(request, 'rbac/forms.html', {
'form_obj': form_obj,
})
# 展示权限信息
class MenuList(View):
def get(self, request):
mid = request.GET.get('mid')
if not mid:
all_permission = models.Permission.objects.all()
else:
all_permission = models.Permission.objects.filter(Q(menu_id=mid) | Q(parent__menu_id=mid))
# all_permission = all_permission.values('id', 'title', 'name', 'url', 'menu_id', 'menu__title', 'parent_id')
permission_dict = {}
for i in all_permission:
menu_id = i.menu_id
id = i.pk
if menu_id:
i.children = []
permission_dict[id] = i
for i in all_permission:
parent_id = i.parent_id
if parent_id:
permission_dict[parent_id].children.append(i)
all_menu = models.Menu.objects.all()
return render(request, 'rbac/menu_list.html', {
'all_menu': all_menu,
'all_permission': permission_dict.values(),
'mid': mid,
})
# 新增编辑菜单
class MenuChange(View):
def get(self, request, menu_id=None, *args, **kwargs):
menu_obj = models.Menu.objects.filter(pk=menu_id).first()
form_obj = MenuForm(instance=menu_obj)
return render(request, 'rbac/menu_form.html', {
'form_obj': form_obj,
})
def post(self, request, menu_id=None, *args, **kwargs):
menu_obj = models.Menu.objects.filter(pk=menu_id).first()
form_obj = MenuForm(instance=menu_obj, data=request.POST)
if form_obj.is_valid():
form_obj.save()
return redirect('rbac:menu_list')
return render(request, 'rbac/menu_form.html', {
'form_obj': form_obj,
})
# 新增编辑权限
class PermissionChange(View):
def get(self, request, permission_id=None, *args, **kwargs):
permission_obj = models.Permission.objects.filter(pk=permission_id).first()
form_obj = PermissionForm(instance=permission_obj)
return render(request, 'rbac/menu_form.html', {
'form_obj': form_obj,
})
def post(self, request, permission_id=None, *args, **kwargs):
permission_obj = models.Permission.objects.filter(pk=permission_id).first()
form_obj = PermissionForm(instance=permission_obj, data=request.POST)
if form_obj.is_valid():
form_obj.save()
return redirect('rbac:menu_list')
return render(request, 'rbac/forms.html', {
'form_obj': form_obj,
})
# 删除
class Delete(View):
def get(self, request, table, pk):
model = getattr(models, table.capitalize())
if not model:
return HttpResponse('非法操作')
obj = model.objects.filter(pk=pk).first()
if not obj:
return HttpResponse('非法操作')
obj.delete()
return redirect(request.META['HTTP_REFERER'])
def multi_permissions(request):
"""
批量操作权限
:param request:
:return:
"""
post_type = request.GET.get('type')
# 编辑 删除
FormSet = modelformset_factory(models.Permission, MultiPermissionForm, extra=0)
# 新增
AddFormSet = formset_factory(MultiPermissionForm, extra=0)
# 数据库所有的权限
permissions = models.Permission.objects.all()
# 路由系统的所有的url 权限
router_dict = get_all_url_dict(ignore_namespace_list=['admin'])
# 数据库权限的name的集合
permissions_name_set = set([i.name for i in permissions])
# 路由系统中权限的name的集合
router_name_set = set(router_dict.keys())
add_name_set = router_name_set - permissions_name_set
add_formset = AddFormSet(initial=[row for name, row in router_dict.items() if name in add_name_set])
if request.method == 'POST' and post_type == 'add':
add_formset = AddFormSet(request.POST)
if add_formset.is_valid():
permission_obj_list = [models.Permission(**i) for i in add_formset.cleaned_data]
query_list = models.Permission.objects.bulk_create(permission_obj_list)
add_formset = AddFormSet()
for i in query_list:
permissions_name_set.add(i.name)
del_name_set = permissions_name_set - router_name_set
del_formset = FormSet(queryset=models.Permission.objects.filter(name__in=del_name_set))
update_name_set = permissions_name_set & router_name_set
update_formset = FormSet(queryset=models.Permission.objects.filter(name__in=update_name_set))
if request.method == 'POST' and post_type == 'update':
update_formset = FormSet(request.POST)
if update_formset.is_valid():
update_formset.save()
update_formset = FormSet(queryset=models.Permission.objects.filter(name__in=update_name_set))
return render(
request,
'rbac/multi_permissions.html',
{
'del_formset': del_formset,
'update_formset': update_formset,
'add_formset': add_formset,
}
)
def distribute_permissions(request):
"""
分配权限
:param request:
:return:
"""
uid = request.GET.get('uid')
rid = request.GET.get('rid')
if request.method == 'POST' and request.POST.get('postType') == 'role':
user = models.UserInfo.objects.filter(id=uid).first()
if not user:
return HttpResponse('用户不存在')
user.roles.set(request.POST.getlist('roles'))
if request.method == 'POST' and request.POST.get('postType') == 'permission' and rid:
role = models.Role.objects.filter(id=rid).first()
if not role:
return HttpResponse('角色不存在')
role.permissions.set(request.POST.getlist('permissions'))
# 所有的用户
user_list = models.UserInfo.objects.all()
# 用户所拥有的角色 id
user_has_roles = models.UserInfo.objects.filter(id=uid).values('id', 'roles')
# 用户所拥有的角色id {角色的id:None }
user_has_roles_dict = {item['roles']: None for item in user_has_roles}
# 所有的角色
role_list = models.Role.objects.all()
if rid:
role_has_permissions = models.Role.objects.filter(id=rid, permissions__id__isnull=False).values('id',
'permissions')
elif uid and not rid:
user = models.UserInfo.objects.filter(id=uid).first()
if not user:
return HttpResponse('用户不存在')
role_has_permissions = user.roles.filter(permissions__id__isnull=False).values('id', 'permissions')
else:
role_has_permissions = []
# 用户 或者 角色所拥有的权限
role_has_permissions_dict = {item['permissions']: None for item in role_has_permissions}
all_menu_list = []
queryset = models.Menu.objects.values('id', 'title') # [ { id title } ]
menu_dict = {}
for item in queryset:
# { id title children:[] }
item['children'] = []
menu_dict[item['id']] = item
all_menu_list.append(item)
other = {'id': None, 'title': '其他', 'children': []}
all_menu_list.append(other)
menu_dict[None] = other
root_permission = models.Permission.objects.filter(menu__isnull=False).values('id', 'title', 'menu_id')
root_permission_dict = {}
for per in root_permission:
# { id title menu_id 'children':[] }
per['children'] = []
nid = per['id']
menu_id = per['menu_id']
root_permission_dict[nid] = per
menu_dict[menu_id]['children'].append(per)
node_permission = models.Permission.objects.filter(menu__isnull=True).values('id', 'title', 'parent_id')
for per in node_permission:
# { id title parent_id }
pid = per['parent_id']
if not pid:
menu_dict[None]['children'].append(per)
continue
root_permission_dict[pid]['children'].append(per)
return render(
request,
'rbac/distribute_permissions.html',
{
'user_list': user_list, # 所有的用户
'role_list': role_list, # 所有的角色
'user_has_roles_dict': user_has_roles_dict, # 用户所拥有的角色
'role_has_permissions_dict': role_has_permissions_dict, # 角色拥有的权限
'all_menu_list': all_menu_list, # 菜单列表
'uid': uid,
'rid': rid
}
)