编程思想之层级变换

1、编程中经常遇到层级关系,例如评论,菜单等

2、如何根据添加时的parent_id,child_id将其变换成真正的层级关系?

3、具体思路可根据对象或者字典里的内存地址引用巧妙将其生成真正的层级数据,将其展现在前端或再进行相应的数据处理

4、菜单生成样例

  1 from django.db import models
  2 
  3 # Create your models here.
  4 from django.db import models
  5 
  6 
  7 class User(models.Model):
  8     """
  9     主要保存用户的账号密码信息,较略
 10     和角色之间是多对多的关系
 11     """
 12     username = models.CharField(max_length=32)
 13     password = models.CharField(max_length=64)
 14 
 15     class Meta:
 16         verbose_name_plural = '用户表'
 17 
 18     def __str__(self):
 19         return self.username
 20 
 21 
 22 class Role(models.Model):
 23     """
 24     角色表,用户和权限解耦,方便权限的批量分配
 25     """
 26     caption = models.CharField(max_length=32)
 27 
 28     class Meta:
 29         verbose_name_plural = '角色表'
 30 
 31     def __str__(self):
 32         return self.caption
 33 
 34 
 35 class User2Role(models.Model):
 36     """
 37     用户角色关系表,可以直接用ManyToMany字段直接生成第三张表
 38     """
 39     u = models.ForeignKey(User)
 40     r = models.ForeignKey(Role)
 41     class Meta:
 42         verbose_name_plural = '用户分配角色'
 43 
 44     def __str__(self):
 45         return "%s:%s"% (self.u.username,self.r.caption)
 46 
 47 
 48 class Permission(models.Model):
 49     """
 50     说是权限表,也可以叫url表,就是菜单栏最后的叶子可以点吧点吧的url,必须绑在特定的菜单后面才可以显示在页面,否则不显示
 51     """
 52     url = models.CharField(max_length=32)
 53     caption = models.CharField(max_length=32)
 54     menu = models.ForeignKey('Menu',related_name='menu_url',null=True,blank=True)
 55     class Meta:
 56         verbose_name_plural = 'url表'
 57 
 58     def __str__(self):
 59         return "%s:%s"% (self.caption,self.url)
 60 
 61 
 62 class Action(models.Model):
 63     """
 64     对数据的粒度,进行了更细一步的分配,增删改查,不同的角色的内容也是不一样的
 65     """
 66     caption = models.CharField(max_length=32)
 67     code = models.CharField(max_length=32)
 68     class Meta:
 69         verbose_name_plural = '操作'
 70 
 71     def __str__(self):
 72         return "%s:%s"% (self.caption,self.code)
 73 
 74 
 75 class Permission2Action(models.Model):
 76     """
 77     这其实才是真正的权限表,对某些内容的增删改查的权限情况
 78     """
 79     p = models.ForeignKey(Permission)
 80     a = models.ForeignKey(Action)
 81     class Meta:
 82         verbose_name_plural = '权限操作表'
 83 
 84     def __str__(self):
 85         return "%s-%s:%s?%s"% (self.p.caption,self.a.caption,self.p.url,self.a.code)
 86 
 87 
 88 class Permission2Action2Role(models.Model):
 89     """
 90     将权限和角色对应起来
 91     """
 92     p2a = models.ForeignKey(Permission2Action)
 93     r = models.ForeignKey(Role)
 94     class Meta:
 95         verbose_name_plural = '角色权限分配表'
 96 
 97     def __str__(self):
 98         return "%s-->%s"% (self.r,self.p2a)
 99 
100 
101 class Menu(models.Model):
102     """
103     所有的菜单,存储的时候有parent,所以其实构造了层级关系
104     """
105     caption = models.CharField(max_length=32)
106     parent = models.ForeignKey('self',related_name='menu_child',null=True,blank=True)
107     class Meta:
108         verbose_name_plural = '菜单表'
109 
110     def __str__(self):
111         if self.parent:
112             return self.caption +'-->'+self.parent.caption
113         else:
114             return self.caption
基于Django的models设计
def index(request):
    print(request.path)
    # user = request.session.get('user',None) #一般是通过session获取用户的用户名,没有的话将其转到login页面
    user_obj = models.User.objects.filter(username='skiler').first()  # 获取当前用户
    user_roles = models.Role.objects.filter(user2role__u__username='skiler')  # 获取当前用户的角色

    # 获取当前用户的所有权限url和method,最好保存在session中,以便用户发来请求做验证,删除权限应当立即更新session
    #增加权限应当让其退出,重新登录再更新session
    permissions = models.Permission2Action.objects.filter(permission2action2role__r__in=user_roles).values('p__url','a__code').distinct()
    # print(permissions)

    #用户拥有的绑定在某个菜单中 权限,筛选条件:要求角色,而且要求绑定在特定的菜单上
    permission_dispaly_list = models.Permission2Action.objects.filter(permission2action2role__r__in=user_roles,p__menu__isnull=False).values('p_id','p__url','p__caption','p__menu').distinct()
    # print(permission_dispaly_list)
    #拿到对象,对其字典的名字做一个改变,以便和菜单部分绑定
    permission_display_dict = {}
    for item in permission_dispaly_list:
        # 对元素的键名做一个改变,以便和菜单的格式一样
        item = {
            'id':item['p_id'],
            'url':item['p__url'],
            'caption':item['p__caption'],
            'parent_id':item['p__menu'],
            'child':[],
            'display':True
        }
        # print(item)
        #将list对象转换成dict,为了根据id对对象内容做更改,构造层级关系,键为其绑定菜单的ID,方便将其绑定至对应的菜单后
        if item['parent_id'] in permission_display_dict:
            permission_display_dict[item['parent_id']].append(item)
        else:
            permission_display_dict[item['parent_id']] = [item,]

    # print(permission_display_dict)
    # for k,v in permission_display_dict.items():
    #     print(k,v)
    #拿到所有的菜单
    menu_list = models.Menu.objects.values('id','caption','parent_id')
    menu_dict = {}
    # 将所有的菜单list转换成dict,为了利用父级关系,方便对象的引用赋值
    # 键为当前菜单的ID,值为该对象,方便基于id赋值
    for item in menu_list:
        item['child'] = []
        item['display'] = False
        menu_dict[item['id']] = item
        menu_dict[item['id']]['display'] = False

    #利用permission绑定的menu_id,即字典的parent_id将其附加在菜单的后边
    for k,item in permission_display_dict.items():
        menu_dict[k]['child'] = item
        menu_dict[k]['display'] = True


    # 将菜单字典的child进行引用赋值,将平级结构构造成深度结构
    # for k,item in menu_dict.items():
    #     print(k,item)


    result = []
    #根据parent_id 和id将平级关系调整为深度关系
    for k,item in menu_dict.items():
        if item['parent_id']:
            menu_dict[item['parent_id']]['child'].append(item)
            if item['display']:
                #加了一些标签display,控制只有叶子节点的菜单的display才为True
                menu_dict[item['parent_id']]['display'] = True
        else:
            # 仅仅将根菜单拿出来,因为子菜单都封装在其内部
            result.append(item)
            
    #将其包含叶子节点的长辈节点,全部使其display变为True,使用while item['parent_id'],代码更为简略
    for k,item in menu_dict.items():
        if item['parent_id']:
            if item['display']:
                menu_dict[item['parent_id']]['display'] = True
    #
    #             result.append(item)
    # print(result)
    menutree = menu_tree(result)




    return render(request,'index.html',{'menutree':menutree})
基于字典的层级变换
def menu_tree(result):
    """
    仅仅是生成html标签方便在前端显示
    """
    response = ""
    tpl = """
    <div class="item">
        <div class="title">%s</div>
        <div class="content">%s</div>
    </div>
    """
    for row in result:
        if 'url' in row:
            response += "<a href = '%s'>%s</a>" %(row['url'],row['caption'])
        else:
            if row['display']:
                title = row['caption']
                content = menu_tree(row['child'])
                response += tpl %(title,content)
    return response
menu_tree函数
 1 def show_result(result,deep = 1):
 2     """
 3     如果直接想看效果,而不是在前端显示,可参考下面代码
 4     :param result: 
 5     :param deep: 
 6     :return: 
 7     """
 8     for item in result:
 9         if item['child']:
10             if item['display']:
11             # print(item['display'])
12                 print('----'* deep, item['caption'],item['display'])
13                 show_result(item['child'],deep=deep+1)
14         else:
15             if item['display']:
16                 # print(item['display'])
17 
18                 print('----'* deep, item['caption'],item['display'])
show_result
#!/usr/bin/env python
# -*- coding: utf8 -*-
#__Author: "Skiler Hao"
#date: 2017/4/10 15:01

menus = \
    [
        {'parent_id': None, 'id': 1, 'caption': '菜单1'},
        {'parent_id': None, 'id': 2, 'caption': '菜单2'},
        {'parent_id': None, 'id': 3, 'caption': '菜单3'},
        {'parent_id': 1, 'id': 4, 'caption': '菜单1.1'},
        {'parent_id': 1, 'id': 5, 'caption': '菜单1.2'},
        {'parent_id': 1, 'id': 6, 'caption': '菜单1.3'},
        {'parent_id': 2, 'id': 7, 'caption': '菜单2.1'},
        {'parent_id': 7, 'id': 8, 'caption': '菜单2.1.1'},
        {'parent_id': 7, 'id': 9, 'caption': '菜单2.1.2'},
        {'parent_id': 7, 'id': 10, 'caption': '菜单2.1.3'}
    ]


class Node(object):
    def __init__(self,nid, caption, url, parent_id):
        self.id = nid
        self.caption = caption
        self.parent_id = parent_id
        self.child = []

        # self.status = status
        #
        # self.open = False

    @classmethod
    def permission2node(cls,permission):
        pass

    @classmethod
    def menu2node(cls,menu):
        menu_node = cls(menu['id'],menu['caption'],None,menu['parent_id'])
        return menu_node
    def __str__(self):
        return str(self.__dict__)


class MenuTree(object):
    @classmethod
    def menus2nodes(self,menus):
        menus_nodes = {}
        for menu in menus:
            node_obj = Node.menu2node(menu)
            menus_nodes[node_obj.id] = node_obj
        return menus_nodes



if __name__ == '__main__':
    menus = \
        [
            {'parent_id': None, 'id': 1, 'caption': '菜单1'},
            {'parent_id': None, 'id': 2, 'caption': '菜单2'},
            {'parent_id': None, 'id': 3, 'caption': '菜单3'},
            {'parent_id': 1, 'id': 4, 'caption': '菜单1.1'},
            {'parent_id': 1, 'id': 5, 'caption': '菜单1.2'},
            {'parent_id': 1, 'id': 6, 'caption': '菜单1.3'},
            {'parent_id': 2, 'id': 7, 'caption': '菜单2.1'},
            {'parent_id': 7, 'id': 8, 'caption': '菜单2.1.1'},
            {'parent_id': 7, 'id': 9, 'caption': '菜单2.1.2'},
            {'parent_id': 7, 'id': 10, 'caption': '菜单2.1.3'}
        ]
    menus_nodes = MenuTree.menus2nodes(menus)

    for menu_node in menus_nodes.values():
        if menu_node.parent_id:
            menus_nodes[menu_node.parent_id].child.append(menu_node)



    print(result)
    # for menu_node in menus_nodes.values():
    #     print(menu_node)
基于类的层级变换

 

posted @ 2017-04-11 11:00  skiler  阅读(244)  评论(0编辑  收藏  举报