django与layui的结合尝试5:权限管理(2)

接上篇:与layui的结合

首先要了解下layui目录的json格式:

{
  "homeInfo": {
    "title": "首页",
    "href": "/static/page/welcome-1.html?t=1"
  },
  "logoInfo": {
    "title": "LAYUI MINI",
    "image": "/static/images/logo.png",
    "href": ""
  },
  "menuInfo": [
    {
      "title": "常规管理",
      "icon": "fa fa-address-book",
      "href": "",
      "target": "_self",
      "child": []
      }
    ]
  }

其实结构跟上篇权限的目录结构一样,差别只在于个别字段,对此修改page表和menu表的字段结构,并赋值

model:

class Menu(models.Model):
    title = models.CharField(max_length=32)
    href = models.CharField(max_length=64,blank=True)
    icon = models.CharField(max_length=32,blank=True)
    target = models.CharField(max_length=32, default="_self")
    father_menu=models.ForeignKey("Menu",null=True,blank=True,on_delete=models.DO_NOTHING)
    class Meta:
        verbose_name_plural = "菜单表"
    def __str__(self):
        return  self.title

class Pages(models.Model):
    target=models.CharField(max_length=32,default="_self")
    icon=models.CharField(max_length=32,blank=True)
    page = models.CharField(max_length=32)
    belong_menu = models.ForeignKey(Menu,on_delete=models.CASCADE)
    def __str__(self):
        return  self.page

由上篇的逻辑处理思想,生成新的pemissionControl.py:


from database.model.permission import *

class pemissionControl(object):

    def __init__(self,user="default"):
        self.user=user
        self.PemissionDict={}
        self.MenuDict={}
        self.LayuiMenuDict={}
        self.LayuiLastMenu={
                "homeInfo": {
                    "title": "首页",
                    "href": "/static/page/welcome-1.html?t=1"
                },
                "logoInfo": {
                    "title": "LAYUI MINI",
                    "image": "/static/images/logo.png",
                    "href": ""
                },
                "menuInfo": []
            }
    def getPemissionDict(self):
        user=User.objects.filter(user_name=self.user).first()
        # 一个用户多角色
        role=Roles.objects.filter(user2role__user=user)
        page_action_obj=list(Role2Page2Actions.objects.filter(role__in=role).distinct().values_list("page_action__page__page","page_action__action__code"))
        # 对权限列表进行判断,如果没有键值就生成列表赋值,否则追加到列表后面
        # 最终字典格式: {'index': ['put', 'get'], 'test.html': ['get']}
        for item in page_action_obj:
            if self.PemissionDict.get(item[0]):
                if self.PemissionDict[item[0]].count(item[1]) == 0:  # 列表去重
                    self.PemissionDict[item[0]].append(item[1])
            else:
                self.PemissionDict[item[0]] = [item[1]]
        return self.PemissionDict

    def createMenuEle(self,menu_id,menu_str,parent_id):
        ele_dict = {
           "menu_id": menu_id,
             "title": menu_str,
         "parent_id": parent_id,
             "child": []
        }
        return ele_dict

    def createPageEle(self,item):
        ele_dict = {
          "title": item,
          "url":""
        }
        return ele_dict


    def InitMenuDict(self):
        # 生成菜单
        # 理论上应该根据菜单多次查找父级菜单,但是那样变成数据库多次操作
        # 因为菜单数量不多,一次取出全部,再根据对象操作更为高效
        menu_obj=Menu.objects.all().values_list("id","title","father_menu")
        for item in menu_obj:
            menu_ele= self.createMenuEle(item[0],item[1],item[2])
            self.MenuDict[str(item[0])]=menu_ele
        return self.MenuDict


    def PageHangToMenu(self):
        # 权限列表的key为页面值,所以可以用keys取有权访问的页面
        PemissionDict=self.getPemissionDict()
        self.InitMenuDict()
        for item in PemissionDict.keys():
            item_ele=self.createPageEle(item)
            MenuPage_obj=list(Menu2Page.objects.filter(page__page=item).values("menu_id"))
            for MenuPage_ele in MenuPage_obj:
                self.MenuDict[str(MenuPage_ele["menu_id"])]["child"].append(item_ele)
        return self.MenuDict

    def creatMenu(self):
        MenuDict=self.PageHangToMenu()
        menu_obj = list(Menu.objects.all().values("id", "father_menu"))
        # 菜单的挂载最新想到的逻辑是从生成的字典中pop子菜单id加入父菜单的child中
        # 但是这样不确定父菜单是不是别的子菜单,如果是,父菜单先挂载后,子菜单会找不到已经被挂载的父菜单
        # 所以就不pop子菜单,而是全部挂载后,再判断菜单节点的child是否为空,从中剔除
        # 而能这样做的原因在于,python对象是同一片内存区域,当你挂载的时候,它并不重新生成对象
        # 所以不会出现子挂父,父还要挂爷的情况
        # 但是还要记录挂载的子菜单id,没挂载过的就是根id,据此删除多余的菜单
        sonId = []
        for menu_ele in menu_obj:
            if menu_ele["father_menu"] :
                sonId.append(menu_ele['id'])
                MenuDict[str(menu_ele["father_menu"])]["child"].append(MenuDict[str(menu_ele["id"])])

        for menu_ele in list(MenuDict.keys()):
            if not MenuDict[menu_ele].get("child"):
                del MenuDict[menu_ele]
            elif int(menu_ele) in sonId:
                del MenuDict[menu_ele]
        return MenuDict
################# layui ###################################
    def createLayuiMenuEle(self,menu_id,menu_str,parent_id,icon,target):
        ele_dict = {
           "menu_id": menu_id,
           "title"  : menu_str,
         "parent_id": parent_id,
             "child": [],
            "icon"  : icon,
            "target": target
        }
        return ele_dict

    def createLayuiPageEle(self,page,target,icon):
        ele_dict = {
        "target"  : target,
          "icon"  : icon ,
          "href"  : page,
        }
        return ele_dict
    def InitLayuiMenuDict(self):
        # 生成菜单
        # 理论上应该根据菜单多次查找父级菜单,但是那样变成数据库多次操作
        # 因为菜单数量不多,一次取出全部,再根据对象操作更为高效
        menu_obj=Menu.objects.all().values_list("id","title","father_menu","icon","target")
        for item in menu_obj:
            menu_ele= self.createLayuiMenuEle(item[0],item[1],item[2],item[3],item[4])
            self.LayuiMenuDict[str(item[0])]=menu_ele
        return self.LayuiMenuDict

    def LayuiPageHangToMenu(self):
        # Layui最后一个菜单只有一个页面,不存在多个页面问题,所以直接用字典update,而不用列表的append
        PemissionDict=self.getPemissionDict()
        self.InitLayuiMenuDict()
        page_ele=Pages.objects.filter(page__in=PemissionDict.keys()).values_list("page","target","icon")
        for item in page_ele:
            item_ele=self.createLayuiPageEle(*item)  # 元组展开用*,字典展开用 **
            MenuPage_obj=list(Menu2Page.objects.filter(page__page=item[0]).values("menu_id"))
            for MenuPage_ele in MenuPage_obj:
                self.LayuiMenuDict[str(MenuPage_ele["menu_id"])].update(item_ele)
        return self.LayuiMenuDict

    def creatLayuiMenu(self):
        LayuiMenuDict=self.LayuiPageHangToMenu()
        menu_obj = list(Menu.objects.all().values("id", "father_menu"))
        sonId=[]
        for menu_ele in menu_obj:
            if menu_ele["father_menu"] :
                sonId.append(menu_ele['id'])
                LayuiMenuDict[str(menu_ele["father_menu"])]["child"].append(LayuiMenuDict[str(menu_ele["id"])])

        for menu_ele in list(LayuiMenuDict.keys()):
            if not LayuiMenuDict[menu_ele].get("child"):
                del LayuiMenuDict[menu_ele]
            elif int(menu_ele) in sonId:
                del LayuiMenuDict[menu_ele]
        # 去掉id,做成列表加到默认的Menuinfo
        for item in LayuiMenuDict:
            self.LayuiLastMenu["menuInfo"].append(LayuiMenuDict[item])
        return self.LayuiLastMenu

    def creatLayuiSimpleMenu(self):
        LayuiMenuDict=self.LayuiPageHangToMenu()
        menu_obj = list(Menu.objects.all().values("id", "father_menu"))
        # 不生成的菜单有两个特性:
        #  1.不是挂载页面的菜单
        #  2.不是别的菜单的父菜单
        # 据此,可以判断id是否在这个范围,然后不做挂载处理,直接continue
        useInMenuId=list(Menu.objects.all().values_list("father_menu",flat=True))
        useInPageId=list(Pages.objects.all().values_list("belong_menu",flat=True))
        sonId=[]
        useInMenuId.extend(useInPageId)
        for menu_ele in menu_obj:
            if menu_ele['id'] not in useInMenuId:
                continue
            if menu_ele["father_menu"] :
                sonId.append(menu_ele['id'])
                LayuiMenuDict[str(menu_ele["father_menu"])]["child"].append(LayuiMenuDict[str(menu_ele["id"])])
        for menu_ele in list(LayuiMenuDict.keys()):
            if not LayuiMenuDict[menu_ele].get("child"):
                del LayuiMenuDict[menu_ele]
            elif int(menu_ele) in sonId:
                del LayuiMenuDict[menu_ele]
        # 去掉id,做成列表加到默认的Menuinfo
        for item in LayuiMenuDict:
            self.LayuiLastMenu["menuInfo"].append(LayuiMenuDict[item])
        return self.LayuiLastMenu

在视图函数里面做一个生成菜单json的接口:

from tools.pemissionControl import *
import json
def print_json(data):
    print(json.dumps(data, sort_keys=True, indent=4, separators=(', ', ': '), ensure_ascii=False))
def getMenu(request):
    user=request.session.get("username")
    per_obj=pemissionControl(user)
    Menu=per_obj.creatLayuiSimpleMenu()
    # Menu=per_obj.creatLayuiMenu()
    print_json(Menu)
    ret=HttpResponse(json.dumps(Menu))
    ret["X-Frame-Options"]="sameorigin"
    return ret

修改index.html中菜单获取页面的url:

把iniUrl的值换成视图getMenu

layui.use(['jquery', 'layer', 'miniAdmin','miniTongji'], function () {
        var $ = layui.jquery,
            layer = layui.layer,
            miniAdmin = layui.miniAdmin,
            miniTongji = layui.miniTongji;

        var options = {
            {#iniUrl: "/static/api/init.json",    // 初始化接口#}
            iniUrl: "/getMenu",    // 初始化接口
            clearUrl: "/static/api/clear.json", // 缓存清理接口
            urlHashLocation: true,      // 是否打开hash定位
            bgColorDefault: false,      // 主题默认配置
            multiModule: true,          // 是否开启多模块
            menuChildOpen: false,       // 是否默认展开菜单
            loadingTime: 0,             // 初始化加载时间
            pageAnim: true,             // iframe窗口动画
            maxTabNum: 20,              // 最大的tab打开数量
        };
        miniAdmin.render(options);

最终效果图:

django与layui的结合尝试5:权限管理(2)

debug:

1.TypeError:format() argument after ** must be a mapping,not list
  元组展开用*,字典展开用 **
  
2.字典添加字典
  dict1.update(dict2)
  
3.unhashable type: 'dict'
  原因:layui的目录是{}包裹起来的字典,也即{{}}
      但是python不支持这样,必须是键值对
  解决:先用{},再赋值完后再加{{}}
  后面发现不加也可以

后言:权限篇的绝大部分功能都已完备,唯一有个问题是因为是一次取出全部菜单,所以不同用户登录并没有隐藏菜单,而只隐藏了页面,若要隐藏菜单,只需修改InitMenuDict(self)函数即可

上一篇:2021-03-02


下一篇:Element-UI里面 Cascader 级联选择器出现的过高问题