day02-菜单处理

解决力度到按钮的级别

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  方案:添加一个新表,permissiongroup,一对多 permission表。

        给permission表新添加一个字段,code字段。为什么加code字段,与不同的url一一对应,方便之后直接拿 code,而不是拿一长串url来堆下面踢掉的request.permission_code进行比对。

from django.db import models

# Create your models here.

class UserInfo(models.Model):
    name=models.CharField(max_length=32,verbose_name='用户名')
    pwd=models.CharField(max_length=32,verbose_name='密码')
    email=models.CharField(max_length=32,verbose_name='邮箱')
    roles=models.ManyToManyField(to='Role')
    def __str__(self):
        return self.name

class Role(models.Model):
    name=models.CharField(max_length=32,verbose_name='角色名')
    permissions=models.ManyToManyField(to='Permission',blank=True)
    def __str__(self):
        return self.name

class PermissionGroup(models.Model):
    name=models.CharField(max_length=32)
    def __str__(self):
        return self.name

class Permission(models.Model):
    name=models.CharField(max_length=32,verbose_name='权限名')
    url=models.CharField(max_length=200,verbose_name='网址',default=None)
    code=models.CharField(max_length=32,default='select')
    permissiongroup=models.ForeignKey(to=PermissionGroup,default=1)
    def __str__(self):
        return self.name

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

init_permission 文件中,初始化生成的格式进行更改。

  直接利用ORM操作拿到的queryset列表并不能满足我们的需要。我们需要下面这种格式的数据。为什么这样做,为了解决粒度问题。

  所以这一步的升级做法是将拿到的queryset表---->两层字典。

{1: {'code': ['select'], 'url': ['/user/select/']},
 2: {'code': ['select'], 'url': ['/order/select/']}}

  permissiongroup_id:{

            "code":[  ],

            "url":[  ],

            }

}

  注释掉的是之前的代码

from django.conf import settings
from pprint import pprint
def init_permission(user,request):
    permission_list = user.roles.filter(permissions__name__isnull=False).values('permissions__name',
                                                                                'permissions__code',
                                                                                'permissions__permissiongroup__id',
                                                                                'permissions__url').distinct()
    print(permission_list)
    '''
    
    
    '''
    # url_list = []
    # for item in permission_list:
    #     url_list.append(item.get('permissions__url'))
    permission_dict={}
    for item in permission_list:
        id=item.get('permissions__permissiongroup__id')
        if id not in permission_dict:
            permission_dict[id]={}
            permission_dict[id]['url']=[item.get('permissions__url')]
            permission_dict[id]['code']=[item.get('permissions__code')]
        else:
            permission_dict[id]['url'].append(item.get('permissions__url'))
            permission_dict[id]['code'].append(item.get('permissions__code'))
    # pprint(permission_dict)

    request.session[settings.PERMISSION_SESSION_KEY] = permission_dict

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

中间件的代码也要随之改动

  

from django.shortcuts import render,redirect,HttpResponse
import re
from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
class RbacMiddleware(MiddlewareMixin):
    def process_request(self,request):
        #1 获取白名单
        permission_valid_url=settings.PERMISSION_VALID_URL
        for url in permission_valid_url:
            if re.match(url,request.path_info):
                return None

        #2 获取权限
        permission_dict = request.session.get(settings.PERMISSION_SESSION_KEY)

        if not permission_dict:
            return HttpResponse('未能读取到该用户的信息')
        #3 对用户请求的url进行匹配
        '''
        {2: {'url': ['/order/select/'], 'code': ['select']}, 1: {'url': ['/user/select/'], 'code': ['select']}}
        '''
        flag=False
        for value in permission_dict.values():
            url_list=value.get('url')
            code_list=value.get('code')
            for reg in url_list:
                reg='^{}$'.format(reg)
                if re.match(reg,request.path_info):
                    flag=True
                    break
            if flag:
                request.permission_code=code_list #给当前用户 赋予 此用户当前组拥有的权限的代号
                break
        if not flag:
            return HttpResponse('无权访问')

        # flag = False
        # for url in url_list:
        #     url='^{}$'.format(url)
        #     if re.match(url, request.path_info):
        #         flag = True
        #         break
        # if not flag:
        #     return HttpResponse('无权访问')
        return None

  有一个很关键的点是,如果在某个permissiongroup中,匹配到用户拥有访问的当前网页的权限,然后,把这个当前网页所属的那个组的所拥有的所有权限的code/url列表赋予到request的某个属性上。

  这样,顺利通过中间件后,所有的不属于白名单的视图函数都可以拿到request的这个属性。

  实现这个功能的核心代码是:

for reg in url_list:
                reg='^{}$'.format(reg)
                if re.match(reg,request.path_info):
                    flag=True
                    break
            if flag:
                request.permission_code=code_list #给当前用户 赋予 此用户当前组拥有的权限的代号
                break

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 在前端实现力度到按钮级别

  视图函数

  

def userselect(request):
    '''
    根据有无权限,
    显示所有的用户信息
    :param request:
    :return:
    '''
    # if request.session.get('url_list'):
    #     url_list=request.session.get('url_list')
    #     flag = False
    #     for url in url_list:
    #         if re.match(url,request.path_info):
    #             flag=True
    #             break
    #     if not flag:
    #         return HttpResponse('无权访问')
    #     users = UserInfo.objects.all()
    #     return render(request, 'userselect.html', {'users': users})
    # else:
    #     return redirect('/login/')
    #


    permission_code=request.permission_code         #中间件给request.permission_code,基于此,实现对力度到按钮级别
    users = UserInfo.objects.all()
    return render(request, 'userselect.html', {'users': users,'permission_code':permission_code})

  

  前端代码

  

<div class="container">
    <div class="row">
        <div class="col-md-offset-3 col-md-5">
            {% if 'add' in permission_code %}
                <span>新增</span>
            {% endif %}
            <table class="table table-bordered table-striped table-hover">
                <thead>
                <tr>
                    <th>序号</th>
                    <th>用户名</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                {% for user in users %}
                    <tr>
                        <td>{{ forloop.counter }}</td>
                        <td>{{ user.name }}</td>
                        <td>
                            {% if 'edit' in permission_code %}
                                <span>编辑</span>
                            {% endif %}
                            {% if 'delete' in permission_code %}
                                <span>删除</span>
                            {% endif %}
                        </td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>

  

  最终效果如下:

  登录名为吴华:

  

  登录名为左国梁:

我就问你牛不牛逼!

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  基于类的封装,继承实现权限封装。很有逼格的东西

   做法:将判辑函数,封装带类中。将对象传给前端。

  优点:代码可以重用。(继承),在不同的页面中,传不同的对象即可。

  假设有这种场景,页面是以上面的html代码写的,每个页面都是如此。看起来没有什么问题。如果code 变动了,那么每个前端的代码都需要改动,这是不应该。

 <td>
                            {% if 'edit' in permission_code %}
                           <span>编辑</span>
                            {% endif %}
                            {% if 'delete' in permission_code %}
         <span>删除</span>
                            {% endif %}
                        </td>

{% if 'edit' in permission_code %}  这段代码太死了。或者说有更好的方法,运用类。

在rbac文件夹下,新建一个permission文件夹,建一个base.py,定义一个BasePermissio类。

class BasePermission(object):
    def __init__(self,codes):
        self.codes=codes

    def select(self):
        if 'select' in self.codes:
            return True
    def delete(self):
        if 'delete' in self.codes:
            return True
    def edit(self):
        if 'edit' in self.codes:
            return True
    def add(self):
        if 'add' in self.codes:
            return True

  在views.py文件视图函数中,导入自定义类,进行以下修改。

from django.shortcuts import render,HttpResponse,redirect
import re
from rbac.models import *
from rbac.service.init_permission import init_permission
from rbac.permission.base import BasePermission

class OrderPermission(BasePermission):
    def report(self):
        if 'report' in self.codes:
            return True

def userselect(request):
    '''
    根据有无权限,
    显示所有的用户信息
    :param request:
    :return:
    '''
    # permission_code=request.permission_code
    permission_obj=BasePermission(request.permission_code)
    users = UserInfo.objects.all()
    # return render(request, 'userselect.html', {'users': users,'permission_code':permission_code})
    return render(request, 'userselect.html', {'users': users,'permission_obj':permission_obj})

def orderselect(request):
    # perminssion_code=request.permission_code
    permission_obj = BasePermission(request.permission_code)
    pass

  注释掉的之前的代码。通过对照,可以看出,之前我们是将拿到的code列表直接传给前端。后面,我们是把类实例化后的对象传给前端,前端拿到这个对象,可以调用类的方法,进行判断。实质性的逻辑是写在类里面的。

  每个前端都接收一个对象,这个对象可以使BasePermission,也可以是继承派生而来的。可以使用父类的,也可以添加自己的。OrderPermission就是这种情况,有属于自己的'report'权限。

   前端代码

<div class="container">
    <div class="row">
        <div class="col-md-offset-3 col-md-5">
            {% if 'add' in permission_code %}
                <span>新增</span>
            {% endif %}
            <table class="table table-bordered table-striped table-hover">
                <thead>
                <tr>
                    <th>序号</th>
                    <th>用户名</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                {% for user in users %}
                    <tr>
                        <td>{{ forloop.counter }}</td>
                        <td>{{ user.name }}</td>
                        <td>
{#                            {% if 'edit' in permission_code %}#}
                            {% if permission_obj.codes.edit %}
                                <span>编辑</span>
                            {% endif %}
{#                            {% if 'delete' in permission_code %}#}
                            {% if permission_obj.codes.delete %}
                                <span>删除</span>
                            {% endif %}
                        </td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  动态生成菜单

  方法1:模板+js,模板继承

    问题:菜单展开和收缩问题:效果会滞后  。

  在templates加入以下模板。

  layout.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        body{
            margin: 0;
        }
        .pg-header{
            height: 48px;
            background-color: chocolate;
        }
    </style>
</head>
<body>
    <div class="pg-header">表头</div>
    <div>
        <div style=" 20%;float: left;background-color: cadetblue">
            <ul>
                <li><a id="m1" href="/menu1/">菜单一</a></li>
                <li><a id="m2" href="/menu2/">菜单二</a></li>
                <li><a id='m3' href="/menu3/">菜单三</a></li>
            </ul>

        </div>
        <div style=" 80%;float: left">
            {% block body %} {% endblock %}


        </div>
    </div>
    {% block js %}{% endblock %}

</body>
</html>

  menu1

{% extends 'layout.html' %}

{% block body %}
    <h1>菜单一内容</h1>
{% endblock %}



{% block js %}
    <script>
        document.getElementById('m1').style.color = 'red';
    </script>
{% endblock %}

  menu2

{% extends 'layout.html' %}

{% block body %}
    <h1>菜二内容</h1>
{% endblock %}


{% block js %}
    <script>
        document.getElementById('m2').style.color = 'red';
    </script>
{% endblock %}

  menu3

{% extends 'layout.html' %}

{% block body %}
    <h1>菜单三内容</h1>
{% endblock %}


{% block js %}
    <script>
        document.getElementById('m3').style.color = 'red';
    </script>
{% endblock %}

  感受:模板继承,就是生成新页面,模板的东西一个不少,在模板的基础上丰富的很多新的东西。 其实就是一个新的html页面。

  当然,url路由也要相应的添加。

  方法2:

  新添加一个新的表---菜单表,是比权限组更高一级的表格。

  权限 新添加一个字段,自关联。select/权限作为父级,add/ delete/ edit/ 是它的儿子。

from django.db import models

# Create your models here.
class Menu(models.Model):
    name=models.CharField(max_length=32)

class UserInfo(models.Model):
    name=models.CharField(max_length=32,verbose_name='用户名')
    pwd=models.CharField(max_length=32,verbose_name='密码')
    email=models.CharField(max_length=32,verbose_name='邮箱')
    roles=models.ManyToManyField(to='Role')
    def __str__(self):
        return self.name

class Role(models.Model):
    name=models.CharField(max_length=32,verbose_name='角色名')
    permissions=models.ManyToManyField(to='Permission',blank=True)
    def __str__(self):
        return self.name

class PermissionGroup(models.Model):
    name=models.CharField(max_length=32)
    menu=models.ForeignKey(to='Menu',default=1)
    def __str__(self):
        return self.name

class Permission(models.Model):
    name=models.CharField(max_length=32,verbose_name='权限名')
    url=models.CharField(max_length=200,verbose_name='网址',default=None)
    code=models.CharField(max_length=32,default='select')
    permissiongroup=models.ForeignKey(to=PermissionGroup,default=1)
    parent=models.ForeignKey(verbose_name='组内可以作为菜单的权限',to='Permission',null=True)
    def __str__(self):
        return self.name

  init_permission 随之改动,将需要的信息放在session的MENU_SESSION_KEY中。

  

from django.conf import settings
from pprint import pprint
def init_permission(user,request):
    # permission_list = user.roles.filter(permissions__name__isnull=False).values('permissions__name',
    #                                                                             'permissions__code',
    #                                                                             'permissions__permissiongroup__id',
    #                                                                             'permissions__url').distinct()

    permission_list = user.roles.filter(permissions__name__isnull=False).values('permissions__id',#权限id
                                                                                'permissions__name',#权限名称
                                                                               'permissions__code',#权限代号
                                                                               'permissions__url',#权限url
                                                                               'permissions__parent__id',#
                                                                               'permissions__permissiongroup__id',#权限所在组的id
                                                                               'permissions__permissiongroup__menu__id',#权限所在组所在菜单id
                                                                               'permissions__permissiongroup__menu__name',#权限所在组所在菜单名称
                                                                                ).distinct()
    # 获取想要的数据,放入session,专门用于生成菜单
    menu_list=[]
    for item in permission_list:
        temp={
            'id':item['permissions__id'],
            'name':item['permissions__name'],
            'code':item['permissions__code'],
            'url':item['permissions__url'],
            'pidi':item['permissions__parent__id'],
            'menu_id':item['permissions__permissiongroup__menu__id'],
            'menu_name':item['permissions__permissiongroup__menu__name'],
        }
        menu_list.append(temp)
    request.session[settings.MENU_SESSION_KEY]=menu_list
    pprint(menu_list)
    # url_list = []
    # for item in permission_list:
    #     url_list.append(item.get('permissions__url'))
    permission_dict={}
    for item in permission_list:
        id=item.get('permissions__permissiongroup__id')
        if id not in permission_dict:
            permission_dict[id]={}
            permission_dict[id]['url']=[item.get('permissions__url')]
            permission_dict[id]['code']=[item.get('permissions__code')]
        else:
            permission_dict[id]['url'].append(item.get('permissions__url'))
            permission_dict[id]['code'].append(item.get('permissions__code'))
    # pprint(permission_dict)

    request.session[settings.PERMISSION_SESSION_KEY] = permission_dict

  更新数据库

  

  输出:

  

[{'code': 'add',
  'id': 2,
  'menu_id': 1,
  'menu_name': '用户管理',
  'name': '添加订单',
  'pidi': 2,
  'url': '/order/add/'},
 {'code': 'edit',
  'id': 3,
  'menu_id': 1,
  'menu_name': '用户管理',
  'name': '编辑订单',
  'pidi': 2,
  'url': '/order/edit/(\d+)'},
 {'code': 'delete',
  'id': 4,
  'menu_id': 1,
  'menu_name': '用户管理',
  'name': '删除订单',
  'pidi': 2,
  'url': '/order/delete/(\d+)'},
 {'code': 'select',
  'id': 5,
  'menu_id': 1,
  'menu_name': '用户管理',
  'name': '查询订单',
  'pidi': None,
  'url': '/order/select/'},
 {'code': 'add',
  'id': 6,
  'menu_id': 1,
  'menu_name': '用户管理',
  'name': '添加用户',
  'pidi': 9,
  'url': '/user/add/'},
 {'code': 'delete',
  'id': 7,
  'menu_id': 1,
  'menu_name': '用户管理',
  'name': '删除用户',
  'pidi': 9,
  'url': '/user/delete/(\d+)'},
 {'code': 'edit',
  'id': 8,
  'menu_id': 1,
  'menu_name': '用户管理',
  'name': '编辑用户',
  'pidi': 9,
  'url': '/user/edit/(\d+)'},
 {'code': 'select',
  'id': 9,
  'menu_id': 1,
  'menu_name': '用户管理',
  'name': '查询用户',
  'pidi': None,
  'url': '/user/select/'}]

  

在视图函数中,读取MENU_SESSION_KEY。转变两次样式,到最终需要的样式。

def userselect(request):
    '''
    根据有无权限,
    显示所有的用户信息
    :param request:
    :return:
    '''
    permission_obj=BasePermission(request.permission_code)
    users = UserInfo.objects.all()

    menu_list=request.session[settings.MENU_SESSION_KEY]
    """
       menu_dict = {
           1: {'id': 1, 'title': '用户列表', 'pid': None, 'url': '/users/', 'menu_id': 1, 'menu_title': '用户管理', "active": True},
           5: {'id': 5, 'title': '订单列表', 'pid': None, 'url': '/orders/', 'menu_id': 2, 'menu_title': '订单管理',"active": False},
           12: {'id': 12, 'title': '角色列表', 'pid': None, 'url': '/roles/', 'menu_id': 1, 'menu_title': '用户管理',"active": False},
           20: {'id': 20, 'title': '送货列表', 'pid': None, 'url': '/deliver/', 'menu_id': 2, 'menu_title': '订单管理',"active": False},
       }
       """
    menu_dict={}
    for item in menu_list:
        pid=item['pid']
        if not pid:
            item['active']=False   #!!!item在这里改动,列表也会随之改动
            menu_dict[item[id]]=item
    for item in menu_list:
        pid=item['pid']
        url = "^%s$" % item['url']
        if re.match(url,request.path_info):
            if pid:
                menu_dict[pid]['active']=True
            else:
                item['active']=True

    """
      menu_dict = {
          1: {
              'title': '用户管理',
              'active': True,
              'children': [
                  {'title': '用户列表', 'url': '/users/', 'active': True},
                  {'title': '订单列表', 'url': '/roles/', 'active': False },
              ]
          },
          2: {
              'title': '订单管理',
              'active': False,
              'children': [
                  {'title': '订单列表', 'url': '/orders/', 'active': False},
                  {'title': '送货列表', 'url': '/deliver/', 'active': False },
              ]
          },
      }
      """
    result={}
    for item in menu_dict:
        menu_id=item['menu_id']
        if menu_id in result:
            temp={'name':item['name'],'url':item['url'],'active':item['active']}
            result[menu_id]['children'].append(temp)
            if item['active']:
                result[menu_id]['active']=True
        else:
            result[menu_id]={
                'name':item['menu_name'],
                'active':item['active'],
                'children':[
                    {'name':item['name'],'url':item['url'],'active':item['active']}
                ]
            }
    return render(request, 'userselect.html', {'users': users,'permission_obj':permission_obj})
原文地址:https://www.cnblogs.com/654321cc/p/8324494.html