• rbac组件之动态二级菜单栏非菜单默认选中


    已经实现了动态二级菜单栏,但是在操作非菜单栏的时候会发现,左侧菜单栏依然存在BUG。那么我们在操作非二级菜单的url时,也需要有一个二级菜单默认被选中。所以还是需要进行优化更改。

    基于上一个版本,对rbac/models.py中的Permission表进行字段整改

    class Permission(models.Model):
        """权限表"""
        title = models.CharField(
            verbose_name="权限名称",
            max_length=32,
        )
        url = models.CharField(
            verbose_name="含正则URL",
            max_length=128,
        )
        menu = models.ForeignKey(
            to="Menu",
            on_delete=models.CASCADE,
            verbose_name="所属二级菜单",
            null=True,  # 并不是所有的url都可以做二级菜单,所以这里需要设置null=True
            blank=True,
            help_text="null表示不是二级菜单,非null表示是二级菜单",
        )
        pid = models.ForeignKey(
            verbose_name="关联的权限",
            to="Permission",
            null=True,  # 如果是二级菜单,那么此字段为空,非二级菜单此字段不为空
            blank=True,
            on_delete=models.CASCADE,
            help_text="对于非菜单权限需要选择一个可以成为菜单的权限,用于做默认展开和选中菜单",
            related_name="parents",
        )
    
        def __str__(self):
            return self.title

    设计思路:在权限初始化的时候,将权限信息的数据结构变化一下,以前的权限信息数据结构为一个列表,列表里只存放了url;现在将权限信息数据结构里的url更改一下,变为一个字典,字典里存放每个url的id,url,pid;二级菜单数据信息也是用字典存储的,为每个二级菜单信息添加一个键为id值为二级菜单id的数据;中间件在判断其是否有权限的过程中为其request设置一个键值对,值为当前url的pid,如果pid为空,那么值为当前的id;在inclusion_tag中,循环每一个二级菜单的时候与request设置的键值对进行比较,比较成功后为其设为默认选中即可

    rbac/service/init_permission.py

    from django.conf import settings
    
    
    def init_permission(current_user, request):
        permission_queryset = current_user.roles.filter(
            permissions__isnull=False,
        ).values(
            "permissions__id",  # 二级菜单id
            "permissions__url",  # 二级菜单url
            "permissions__title",  # 二级菜单名称
            "permissions__menu__id",  # 一级菜单ID
            "permissions__menu__title",  # 一级菜单名称
            "permissions__menu__icon",  # 一级菜单图标
            "permissions__pid_id",  # 非菜单url的自关联pid值,如果是二级菜单那么该字段值为空
        ).distinct()
        permission_list = []  # 存放用户的权限数据信息
        menu_dict = {}  # 存放菜单信息
        for item in permission_queryset:
            # 权限信息数据结构变为列表套字典
            permission_list.append(
                {
                    "id": item["permissions__id"],  # 如果是二级菜单,那么需要用到此字段与菜单信息进行比较
                    "url": item["permissions__url"],
                    "pid": item["permissions__pid_id"],  # 如果此字段为空,那么说明该url是二级菜单;如果不为空,那么需要用此字段与菜单信息进行比较
                }
            )
            menu_id = item["permissions__menu__id"]  # 取到二级菜单所对应的一级菜单的id,如果不能做二级菜单的,那么这个字段值是null
            if not menu_id:
                continue
            node = {
                "id": item["permissions__id"],
                "title": item["permissions__title"],
                "url": item["permissions__url"],
            }  # 二级菜单数据信息,方便后续添加
            if menu_id in menu_dict:
                menu_dict[menu_id]["children"].append(node)  # 如果一级菜单已经存在,那么直接添加二级菜单数据信息即可
            else:
                # 如果一级菜单不存在,那么还需要添加一级菜单的数据信息和二级菜单的数据信息
                menu_dict[menu_id] = {
                    "title": item["permissions__menu__title"],
                    "icon": item["permissions__menu__icon"],
                    "children": [node, ],
                }
        request.session[settings.PERMISSION_SESSION_KEY] = permission_list
        request.session[settings.MENU_SESSION_KEY] = menu_dict
    
    
    """
    permission_list = [{'id': 1, 'url': '/customer/list/', 'pid': None},
                        {'id': 2, 'url': '/customer/add/', 'pid': 1},
                        {'id': 3, 'url': '/customer/list/(?P<cid>\\d+)/', 'pid': 1},
                        {'id': 4, 'url': '/customer/edit/(?P<cid>\\d+)/', 'pid': 1},
                        {'id': 5, 'url': '/customer/import/', 'pid': 1},
                        {'id': 6, 'url': '/customer/tpl/', 'pid': 1},
                        {'id': 7, 'url': '/payment/list/', 'pid': None},
                        {'id': 8, 'url': '/payment/add/', 'pid': 7},
                        {'id': 9, 'url': '/payment/del/(?P<pid>\\d+)/', 'pid': 7},
                        {'id': 10, 'url': '/payment/edit/<?P<pid>\\d+/', 'pid': 7}]
    menu_dict = {'1': {
                        'title': '信息管理', 
                        'icon': 'fa-camera-retro',
                        'children': [{'id': 1, 'title': '客户列表', 'url': '/customer/list/'}]},
                '2': {
                        'title': '用户管理', 
                        'icon': 'fa-fire',
                        'children': [{'id': 7, 'title': '账单列表', 'url': '/payment/list/'}]}}
    """

    rbac/middlewares/rbac.py

    from django.utils.deprecation import MiddlewareMixin
    from django.conf import settings
    import re
    from django.shortcuts import HttpResponse
    
    
    class RbacMiddleware(MiddlewareMixin):
        def process_request(self, request):
            current_url = request.path_info
            for url in settings.VALID_URL_LIST:
                if re.match(url, current_url):
                    return None
            permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
            if not permission_list:
                return HttpResponse("未获取到用户权限信息,请重新登录...")
            flag = False
            for url in permission_list:
                reg = "^%s$" % url["url"]
                if re.match(reg, current_url):
                    flag = True
                    request.current_selected_permission = url["pid"] or url["id"]  # pid不为空,那么值为pid;pid为空,那么值为id
                    break
            if not flag:
                return HttpResponse("你无权访问,请速速撤离...")
    
    
    """
    permission_list = [{'id': 1, 'url': '/customer/list/', 'pid': None},
                        {'id': 2, 'url': '/customer/add/', 'pid': 1},
                        {'id': 3, 'url': '/customer/list/(?P<cid>\\d+)/', 'pid': 1},
                        {'id': 4, 'url': '/customer/edit/(?P<cid>\\d+)/', 'pid': 1},
                        {'id': 5, 'url': '/customer/import/', 'pid': 1},
                        {'id': 6, 'url': '/customer/tpl/', 'pid': 1},
                        {'id': 7, 'url': '/payment/list/', 'pid': None},
                        {'id': 8, 'url': '/payment/add/', 'pid': 7},
                        {'id': 9, 'url': '/payment/del/(?P<pid>\\d+)/', 'pid': 7},
                        {'id': 10, 'url': '/payment/edit/<?P<pid>\\d+/', 'pid': 7}]
    """

    rbac/templatetags/rbac.py

    from django.template import Library
    from django.conf import settings
    import re
    from collections import OrderedDict
    
    
    register = Library()
    
    
    @register.inclusion_tag("rbac/menu.html")
    def menu(request):
        menu_dict = request.session.get(settings.MENU_SESSION_KEY)  # 从session中取出信息
        key_list = sorted(menu_dict)  # 对字典的key进行排序
        ordered_dict = OrderedDict()  # 建立一个有序空字典(按存入顺序排序,先存入的在前面)
        for key in key_list:
            val = menu_dict[key]  # 取到一级菜单的所有数据信息
            val["class"] = "hide"  # 添加一个class键,值为hide;这个class属性是二级菜单引用的,并不是一级菜单使用;在前端的效果为所有二级菜单class都有hide属性值,即隐藏所有二级菜单
    
            for per in val["children"]:  # 循环当前一级菜单下的每个二级菜单
                if per["id"] == request.current_selected_permission:  # 取到request设置的值,与二级菜单的id值进行比较
                    per["class"] = "active"  # 为匹配成功的二级菜单的a标签添加一个class键,值为active;在前端的效果为此二级菜单为激活的状态,即被选中的效果
                    val["class"] = ""  # 把匹配成功的二级菜单的直属一级菜单,class键的值改为空,即这个二级菜单的class没有了hide属性值,也就会展开显示所有二级菜单
            ordered_dict[key] = val  # 将更改一级菜单所有数据信息,根据key和val存放到有序空字典中
        return {"menu_dict": ordered_dict}
    
    
    """
    menu_dict = {'1': {
                        'title': '信息管理', 
                        'icon': 'fa-camera-retro',
                        'children': [{'id': 1, 'title': '客户列表', 'url': '/customer/list/'}]},
                '2': {
                        'title': '用户管理', 
                        'icon': 'fa-fire',
                        'children': [{'id': 7, 'title': '账单列表', 'url': '/payment/list/'}]}}
    """

    总结:动态二级菜单栏非菜单默认选中,在Permission中加了一个pid字段,该字段自关联Permission表,这样就可以把非二级菜单url与二级菜单url创建关联关系;权限初始化的时候,将权限数据信息结构更改为列表套字典,每个字典存储着该权限的url和id及pid,这样方便与二级菜单进行比对,比对上了将对应二级菜单设置为默认选中即可。

    """
    权限信息
    permission_list = [{'id': 1, 'url': '/customer/list/', 'pid': None},
                        {'id': 2, 'url': '/customer/add/', 'pid': 1},
                        {'id': 3, 'url': '/customer/list/(?P<cid>\\d+)/', 'pid': 1},
                        {'id': 4, 'url': '/customer/edit/(?P<cid>\\d+)/', 'pid': 1},
                        {'id': 5, 'url': '/customer/import/', 'pid': 1},
                        {'id': 6, 'url': '/customer/tpl/', 'pid': 1},
                        {'id': 7, 'url': '/payment/list/', 'pid': None},
                        {'id': 8, 'url': '/payment/add/', 'pid': 7},
                        {'id': 9, 'url': '/payment/del/(?P<pid>\\d+)/', 'pid': 7},
                        {'id': 10, 'url': '/payment/edit/<?P<pid>\\d+/', 'pid': 7}]
    菜单信息
    menu_dict = {'1': {
                        'title': '信息管理', 
                        'icon': 'fa-camera-retro',
                        'children': [{'id': 1, 'title': '客户列表', 'url': '/customer/list/'}]},
                '2': {
                        'title': '用户管理', 
                        'icon': 'fa-fire',
                        'children': [{'id': 7, 'title': '账单列表', 'url': '/payment/list/'}]}}
    """

    链接:https://pan.baidu.com/s/1X48Vnh4i7Ns36vbVLCyrCA
    提取码:abab

  • 相关阅读:
    Hadoop2.0 HA集群搭建步骤
    了解何为DML、DDL、DCL
    搭建Hadoop平台(新手入门)
    周记1
    IT小小鸟
    Python中的函数修饰符
    python_类方法和静态方法
    Python的log模块日志写两遍的问题
    python——装饰器例子一个
    初识HIVE
  • 原文地址:https://www.cnblogs.com/xuewei95/p/15859039.html
Copyright © 2020-2023  润新知