Django 权限管理(二)

权限菜单展示

1.展示的效果

Django 权限管理(二)

实现该效果,涉及到的核心点(突破点)

1. 权限分类:

(1)菜单权限 (主要用来展示的权限(url)如查看权限 url,  如上图中的每个权限都归类为菜单权限,主要用来构建权限列表)。

(2)按钮权限(主要用来 添加,删除,修改 的权限url  )。

(3) 实现 权限分类的方法。

在权限model类 Permission ,设置一个 type 字段  type = model.Charfield(max_length=32,choices=(("menu","菜单权限"),(“button”,"按钮权限")),default="menu"))

后端通过该type 字段判断哪些权限需要展示。

2. 权限的树形结构。构建  权限分层,就是权限归类。如  信息管理 权限下有---  客户管理权限,订单管理权限,客户管理权限下有查看客户权限,订单管理权限下有查看订单。

(1)自关联。

在权限model类Permission 中设置一个字段自关联字段,类似一对多 的情况,就是一个父权限可以多个子权限, 该字段用于记录每个权限的父权限id

#自关联,解决 层级关系问题,谁是谁的子权限 ,树形结构
parent = models.ForeignKey("Permission",on_delete=models.CASCADE,null=True,blank=True) #null = True 该权限的父权限可以为空, blank=True 添加该权限时可以不填内容。

 (2)权限列表树形结构数据的构建

需要通过菜单权限列表,构建出以下类型的数据结构,如何实现。

 default_data = [
        {
            "text": '信息管理',
            "href": '',
            "tags": ['2'],
            "nodes": [
                {
                    "text": '客户管理',
                    "href": '',
                    "tags": ['1'],
                    "nodes": [
                        {
                            "text": '查看客户',
                            "href": '/stark/app1/customer/',
                            "tags": ['0']
                        },
                    ]
                },
                {
                    "text": '订单管理',
                    "href": '',
                    "tags": ['1'],
                    "nodes": [
                        {
                            "text": '查看订单',
                            "href": '/stark/app1/order/',
                            "tags": ['0']
                        },
                    ]
                },
            ]
        },

        {
            "text": '权限管理',
            "href": '',
            "tags": ['0']
        },

    ]

实现逻辑:

1. 用户登录成功后,获取该用户的权限,并构建权限列表

def permission_init(request,user_obj):
   queryset = user_obj.roles.all().values( "permissions__title", # 跨表 "permissions__url", "permissions__type", "permissions__parent", "permissions__id", ).distinct() # [{'permissions__title': '查看客户', 'permissions__url': '/stark/app1/customer/'},.....] # 准备一个权限列表,用来装用户所有的权限 permissions_list = [] for per in queryset: permissions_list.append( { "url": per.get("permissions__url"), "title": per.get("permissions__title"), "type": per.get("permissions__type"), "pid": per.get("permissions__parent"), "id": per.get("permissions__id"), } ) # 将权限列表注入session ,用户下次登录可以获取到该权限列表 request.session["permission_list"] = permissions_list

2.获取用户的权限列表

(1)先经过,权限类型过滤,判断是菜单权限还是按钮权限,过滤出菜单权限,用于展示到页面。

(2)通过 菜单权限列表,构建 树形结构数据(前端需要展示的树形结构数据)

(3)权限列表路径展开问题的解决

难点在于如何给当前选择的路径的所有父权限,设置属性。state :{“expanded”:True}

from django import template
import json
register = template.Library()

@register.inclusion_tag("rbac/menu.html")
def get_menu(request):
    permission_list = request.session.get("permission_list") #获取用户的权限列表
    per_list = []
    permission_dic ={}
    #循环重构字典 , 每一个权限的id 为键,新字典为value ,构成一个新的字典
    for per_dic in permission_list:
        new_per_dic = {}
        #剔除非菜单权限
        if per_dic.get("type") == "button":
            continue
        #对菜单权限,重构数据结构,以id 值为键,新数据结构为value值
        new_per_dic["text"] = per_dic["title"]
        new_per_dic["href"] =  per_dic["url"] or ""
        new_per_dic["pid"] = per_dic["pid"] or ""
        new_per_dic["nodes"] = []
        #new_per_dic["state"] = {"expanded":True} #此属性 设置节点展开 ,bootstrap_view 插件中的语法state :expanded为True 时节点时展开的
        permission_dic[per_dic.get("id")] = new_per_dic

    #重构出的字典大致结构如下:
    # permission_dic={
    #     1:{"text":"信息管理","href":"","nodes":[],"pid":None},
    #     2:{"text":"权限管理","href":"","nodes":[],"pid":None},
    #     3:{"text":"客户管理","href":"","nodes":[],"pid":1},
    #     4:{"text":"订单管理","href":"","nodes":[],"pid":1},
    #     6:{"text":"查看客户","href":"","nodes":[],"pid":3},
    #     7:{"text":"查看订单","href":"","nodes":[],"pid":4},
    # }

# 路径展开 current_path = request.path #过滤出一级权限,也就是 父pid 为None 的权限 ,加入已经准备好的数据列表 for id,dic in permission_dic.items(): #找到根节点,也就是一级权限 if not dic["pid"]: per_list.append(dic) else: permission_dic[dic["pid"]]["nodes"].append(dic) #路径展开逻辑 if dic["href"]==current_path: #找到当前的路径 pid =dic["pid"] #找到当前路径的父级 pid #通过while 循环不断的往上找父及诶单,并将父节点设置 相关的属性 while pid: permission_dic[pid]["state"] = {"expanded":True} pid = permission_dic[pid]["pid"] #为什么需要用json格式的,因为python代码 None,Ture 在js 语法中不能识别,所以需要转化成json字符串给插件 return {"default_data": json.dumps(per_list)}

inclusion_tag("filename.html")

1.  该自定义标签,返回必须是一个字典形式,一般该方法需要带参数reuqest , 是通过render 方法进行渲染的。在全局可以应用,该标签。

2. 应用场景,权限菜单栏,在增,删,改,查页面都需要通过动态数据渲染出菜单权限栏时,用此中方法可以解决代码重复问题。

3. 实现 效果: 给调用该方法的页面返回一个html 前端标签代码。相当于 通过该标签方法,得到后端数据,返回字典格式的数据,该数据

在  filename.html 渲染后,将该filename.html后的效果返回给前端页面调用该方法的位置。

Django 权限管理(二)

该方法写在 上图rbac.py 中:

from django import template
import json
register = template.Library()

@register.inclusion_tag("rbac/menu.html")
def get_menu(request):
    permission_list = request.session.get("permission_list")
    per_list = []
    permission_dic ={}
    #循环重构字典 , 每一个权限的id 为键,新字典为value ,构成一个新的字典
    for per_dic in permission_list:
        new_per_dic = {}
        #剔除非菜单权限
        if per_dic.get("type") == "button":
            continue
        #对菜单权限,重构数据结构,以id 值为键,新数据结构为value值
        new_per_dic["text"] = per_dic["title"]
        new_per_dic["href"] =  per_dic["url"] or ""
        new_per_dic["pid"] = per_dic["pid"] or ""
        new_per_dic["nodes"] = []
        #new_per_dic["state"] = {"expanded":True} #此属性 设置节点展开 ,bootstrap_view 插件中的语法state :expanded为True 时节点时展开的
        permission_dic[per_dic.get("id")] = new_per_dic

    #重构出的字典大致结构如下:
    # permission_dic={
    #     1:{"text":"信息管理","href":"","nodes":[],"pid":None},
    #     2:{"text":"权限管理","href":"","nodes":[],"pid":None},
    #     3:{"text":"客户管理","href":"","nodes":[],"pid":1},
    #     4:{"text":"订单管理","href":"","nodes":[],"pid":1},
    #     6:{"text":"查看客户","href":"","nodes":[],"pid":3},
    #     7:{"text":"查看订单","href":"","nodes":[],"pid":4},
    # }
        # 路径展开
    current_path = request.path

    #过滤出一级权限,也就是 父pid 为None 的权限 ,加入已经准备好的数据列表
    for id,dic in permission_dic.items():
        #找到根节点,也就是一级权限
        if not dic["pid"]:
            per_list.append(dic)
        else:
            permission_dic[dic["pid"]]["nodes"].append(dic)

        #路径展开逻辑
        if dic["href"]==current_path: #找到当前的路径
            pid =dic["pid"] #找到当前路径的父级 pid
            #通过while 循环不断的往上找父及诶单,并将父节点设置 相关的属性


            while pid:
                permission_dic[pid]["state"] = {"expanded":True}
                pid = permission_dic[pid]["pid"]
#为什么需要用json格式的,因为python代码 None,Ture 在js 语法中不能识别,所以需要转化成json字符串给插件
    return {"default_data": json.dumps(per_list)}
@register.inclusion_tag("rbac/menu.html")

menu.html:(该标签html ,参数文件)

<div id="treeview" class="small">

</div>


<script src="/static/bootstrap-treeview/js/bootstrap-treeview.js"></script>

<script type="text/javascript">
    // API文档参数列表: https://www.cnblogs.com/tangzeqi/p/8021637.html

    $(function () {

        var options = {
            data:{{ permission_tree_list|safe }}, //data属性是必须的,是一个对象数组    Array of Objects.
            color: "", //所有节点使用的默认前景色,这个颜色会被节点数据上的backColor属性覆盖.        String
            backColor: "#22282e", //所有节点使用的默认背景色,这个颜色会被节点数据上的backColor属性覆盖.     String
            borderColor: "#000000", //边框颜色。如果不想要可见的边框,则可以设置showBorder为false。        String
            nodeIcon: "glyphicon glyphicon-stop", //所有节点的默认图标
            checkedIcon: "glyphicon glyphicon-check", //节点被选中时显示的图标         String
            collapseIcon: "glyphicon glyphicon-minus", //节点被折叠时显示的图标        String
            expandIcon: "glyphicon glyphicon-plus", //节点展开时显示的图标        String
            emptyIcon: "glyphicon", //当节点没有子节点的时候显示的图标              String
            enableLinks: false, //是否将节点文本呈现为超链接。前提是在每个节点基础上,必须在数据结构中提供href值。        Boolean
            highlightSearchResults: true, //是否高亮显示被选中的节点        Boolean
            levels: 2, //设置整棵树的层级数  Integer
            multiSelect: false, //是否可以同时选择多个节点      Boolean
            onhoverColor: "#F5F5F5", //光标停在节点上激活的默认背景色      String
            selectedIcon: "glyphicon glyphicon-stop", //节点被选中时显示的图标     String

            searchResultBackColor: "", //当节点被选中时的背景色
            searchResultColor: "", //当节点被选中时的前景色

            selectedBackColor: "", //当节点被选中时的背景色
            selectedColor: "#FFFFFF", //当节点被选中时的前景色

            showBorder: true, //是否在节点周围显示边框
            showCheckbox: false, //是否在节点上显示复选框
            showIcon: true, //是否显示节点图标
            showTags: false, //是否显示每个节点右侧的标记。前提是这个标记必须在每个节点基础上提供数据结构中的值。
            uncheckedIcon: "glyphicon glyphicon-unchecked", //未选中的复选框时显示的图标,可以与showCheckbox一起使用
        };

        $('#treeview').treeview({

            color: "white",
            onhoverColor:"#394555",
            selectedColor:"white",
            showBorder: false,
            expandIcon: 'glyphicon glyphicon-chevron-right',
            collapseIcon: 'glyphicon glyphicon-chevron-down',
            nodeIcon: 'glyphicon glyphicon-bookmark',
            enableLinks: true,
            levels: 1,
            showIcon: false,
            selectedBackColor: "",
            backColor: "#22282e",
            highlightSearchResults: true,
            data: {{ permission_tree_list|safe }},
        });


        $('#treeview').on('nodeSelected', function (event, data) {
            console.log(data);
        })

    });
</script>
menu.html

base.html :(调用该标签的文件)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    {% block title %}
        <title>Starter Template for Bootstrap</title>
    {% endblock title %}
    <link href="/static/bootstrap/css/bootstrap.css" rel="stylesheet">
    <link rel="stylesheet" href="/static/css/site.css">
    <script src="/static/js/jquery-3.3.1.js"></script>



</head>

<body>

<div class="container-fluid site-wrapper">

    <!-- site-navbar -->
    <div class="site-navbar navbar-fixed-top">
        <span class="site-navbar-title">CRM后台管理</span>
    </div> <!-- /.site-navbar -->


    <div class="row">

        <!-- site-menu -->
        <div class="site-menu">

            <p class="site-menu-title">主菜单</p>

            {% block side_bar %}
                {% load rbac %}

                {% get_menu request %}
            {% endblock side_bar %}


        </div> <!-- /.site-menu -->


        <!-- site-body -->
        <div class="site-body">

            <div class="site-content-wrap">

                {% block content %}

                {% endblock content %}

            </div>

        </div> <!-- site-body -->

    </div>


</div><!-- /.container-fluid -->



<script src="/static/bootstrap/js/bootstrap.js"></script>

<script src="/static/js/site.js"></script>

{% block script %}

{% endblock script %}

</body>
</html>
base.html 调用该标签的文件

相关推荐