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

权限管理可以分成两部分:

(1)对页面的访问权限,对此可以生成字典列表,类似:

{'index': ['put', 'get'], 'test.html': ['get']}

这样的形式,以后访问某个页面可以利用键值对匹配取得权限比对

(2)菜单的生成与显示,类似:

{
    "3": {
        "child": [
            {
                "title": "test.html", 
                "url": ""
            }, 
            {
                "child": [
                    {
                        "title": "test.html", 
                        "url": ""
                    }, 
                    {
                        "child": [], 
                        "menu_id": 8, 
                        "parent_id": 6, 
                        "title": "菜单1.1.1"
                    }
                ], 
                "menu_id": 6, 
                "parent_id": 3, 
                "title": "菜单1.1"
            }, 
            {
                "child": [], 
                "menu_id": 7, 
                "parent_id": 3, 
                "title": "菜单1.2"
            }
        ], 
}

用child组成菜单树形结构,从而生成菜单

由此,我们必然要有数据库存放,菜单与页面、用户、角色、权限之间的关系

这里以满足第一范式的原则,将权限部分数据库设计如下:

permission.py


from database.model.models import *
class Roles(models.Model):
    roles_name=models.CharField(max_length=16)
    class Meta:
        verbose_name_plural = "角色表"
    def __str__(self):
        return self.roles_name

class Actions(models.Model):
    action = models.CharField(max_length=16)
    code = models.CharField(max_length=16)
    class Meta:
        verbose_name_plural = "操作"
    def __str__(self):
        return  "%s - %s" %(self.action,self.code)

class Menu(models.Model):
    title = models.CharField(max_length=32)
    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):

    page = models.CharField(max_length=32)
    belong_menu = models.ForeignKey(Menu,on_delete=models.CASCADE)
    def __str__(self):
        return  self.page

class User2Role(models.Model):
    user=models.ForeignKey(User,on_delete=models.CASCADE)
    role=models.ForeignKey(Roles,on_delete=models.CASCADE)
    def __str__(self):
        return  "%s-%s" %(self.user,self.role)

class Page2Actions(models.Model):
    page=models.ForeignKey(Pages,on_delete=models.CASCADE)
    action=models.ForeignKey(Actions,on_delete=models.CASCADE)
    class Meta:
        verbose_name_plural = "页面权限表"
    def __str__(self):
        return "%s - %s" %(self.page,self.action)

class Role2Page2Actions(models.Model):
    role=models.ForeignKey(Roles,on_delete=models.CASCADE)
    page_action=models.ForeignKey(Page2Actions,on_delete=models.CASCADE)
    class Meta:
        verbose_name_plural = "角色权限表"
    def __str__(self):
        return "%s -%s-%s" %(self.role,self.page_action.page,self.page_action.action)


class Menu2Page(models.Model):
    page=models.ForeignKey(Pages,on_delete=models.CASCADE)
    menu=models.ForeignKey(Menu,on_delete=models.CASCADE)

    def __str__(self):
        return  "%s-%s" %(self.menu,self.page)

权限部分比较简单,当进来一个用户,判断它的角色,再由角色取得页面权限关系去重,组成字典后返回,核心代码如下:

  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

生成菜单比较困难,当你组合菜单与菜单之间的关系时,需要索引,开始我以名称来,实现起来比较困难,后来参考:https://blog.csdn.net/Ayhan_huang/article/details/78094570?locationNum=9&fps=1,利用id来做索引,把菜单分成三部分来实现:

(1)格式化菜单 (2)挂载页面 (3)菜单生成,最终比较顺利地实现了

 

最终代码如下:

permissionControl.py

from database.model.permission import *

class pemissionControl(object):

    def __init__(self,user="default"):
        self.user=user
        self.PemissionDict={}
        self.MenuDict={}
    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对象是同一片内存区域,当你挂载的时候,它并不重新生成对象
        # 所以不会出现子挂父,父还要挂爷的情况
        for menu_ele in menu_obj:
            if menu_ele["father_menu"] :
                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]
        return MenuDict

视图部分的测试操作:

from tools.pemissionControl import *
import json
def print_json(data):
    print(json.dumps(data, sort_keys=True, indent=4, separators=(', ', ': '), ensure_ascii=False))
def test(request):
    user="linzb"
    per_obj=pemissionControl(user)
    PemissionDict=per_obj.getPemissionDict()
    print_json(per_obj.getPemissionDict())
    print_json(per_obj.creatMenu())
    return HttpResponse("...")

debug:

1.database.Menu.father_menu: (fields.E303) Reverse query name for 'database.Menu.father_menu' *es with field name 'database.Menu.menu'.
  HINT: Rename field 'database.Menu.menu', or add/change a related_name argument to the definition for field 'database.Menu.father_menu'.
  
原因:定义数据库时库名与字段名一样
class Menu(models.Model):
    menu = models.CharField(max_length=32)
  
解决:
class Menu(models.Model):
    title = models.CharField(max_length=32)
  

2.ValueError: The QuerySet value for an exact lookup must be limited to one result using slicing.
原因:查询的结果是一个结果集(QuerySet),在Student.objects.filter(student=user)的筛选条件必须是一个对象而不是一个结果集。
解决:+.first()


3.ValueError: dictionary update sequence element #0 has length 1; 2 is required

  解决,dict()改为eval()

4.dictionary changed size during iteration
 
  字典在遍历时不能进行修改,建议转成列表或集合处理。
例子:
  for key in list(a.keys()):
    del a[key]
上一篇:qt学习——QMainWindow


下一篇:一手遮天 Android - view(导航类): ToolBar 基础