一, 引入
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 } )