单爆手

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
    1. 昨日补充
        1. 当一个用户有多种角色的时候,查询出来的权限列表有重复的选项,要去重
            QuerySet对象.distinct()   --> 复习下必知必会13条
    2. 动态生成菜单
        1. 利用额外的字段把能作为菜单显示的权限标识出来
            1. 登陆成功之后获取菜单信息
            2. 在模板语言中动态生成菜单
        2. 动态菜单的问题
            1. active
                1. 判断当前访问的URL和菜单的URL如果一致就加上active样式
            2. 模板语言中把session key写死
                1. 自定义inclusion_tag    --> 在模板语言中遇到返回一截用到了变量的HTML代码
    3. 功能封装
        1. 封装加载权限信息和菜单信息的代码到一个单独的模块
        2. 把inclusion_tag放到rbac app下面
        3. 把静态文件都剥离到rbac app下面
    4. RBAC组件使用方法
        1. 复制RBAC APP到项目中,注册
        2. 在settings中配置权限组件相关的配置
        3. 修改表结构,创建表结构
            0. 清空开发阶段miagrations中的变更记录
            1. python manage.py makemigrations
            2. python manage.py migrate
        4. 录入权限信息
        5. 在登录业务逻辑中执行权限初始化
        6. 注册中间件实现权限的校验
        7. 使用菜单 inclusion_tag
    
自主学习能力
    1. xlrd
    2. Django上传文件的那些参数

补充:

何思浩 boss  hsh12345

王帅  经理     ws12345
李庆阳 销售   lqy12345
袁成明 实习    ycm12345

  当一个用户有多种角色如用户何思浩即boss是经理又是销售,那views.py中视图函数得到的权限信息url列表会有很多且容易有重复,如图1,所以要去重,但它是filter查询出来的queryset类型--所以queryset对象.distinct()方法即可.

一.动态菜单功能实现

  此前写的效果中,/customer/list/页面中,不管某用户有没有权限,帐单管理这项都在左侧栏显示,如用户李庆阳一登录,他其实是没有帐单管理权限(点不了),但是这个菜单却一直放在他的页面中,这样不合理,--所以左侧菜单栏不应该写死,应根据登录的用户不同,来只显示用户有权限的那些菜单.如下图2中,有这么多权限,其实客户管理的这些权限可以放在图1客户管理列表处,帐单管理这些权限可以放在图1帐单管理列表处.所以图1中的两菜单应该与图2中这些权限应该有关联,且这么多权限有的可以作为菜单放图1菜单栏中,有的不能如编辑/删除客户就不能因为编辑要指定具体的某客户id---那怎么办?--在这些权限后再加一字段是否能做菜单,是就打勾,然后我每一次查询当前用户权限时,把是菜单的那些权限找出来,那这样就能获取到这些菜单信息,并在页面上循环展示即可。

        

1.把能做为菜单的权限标识出并给它添加图标样式

(1)修改models.py:

  给权限表加字段,且菜单上有图标,所以我把图标也当一字段存进来,

 

 python manage.py makemigrations----python manage.py migrate

(2)rabc/admin.py中:

(3)效果如下图,然后到fontawesome图标网找几个图标填入如下权限图标字段中:

   此前的菜单是写死的,就只有客户管理和帐单管理两菜单. 现在我需要对它做自定制,菜单不只这两个,而是根据登录用户的不同的不同权限显示不同菜单--动态菜单--前提得知道此用户有哪些菜单(能做菜单的权限)--视图查询中可以找到。

2.展示动态菜单---不同用户有不同菜单展示

(1)rabc/views.py中:

  查询中先取到权限列表,再取可以作为菜单列表。

from django.shortcuts import render, redirect, HttpResponse
from rbac.models import UserInfo
from django.conf import settings
# Create your views here.


def login(request):
    error_msg = ''
    if request.method == 'POST':
        username = request.POST.get('username')
        pwd = request.POST.get('password')
        user_obj = UserInfo.objects.filter(username=username, password=pwd).first()
        if user_obj:
            # 登陆成功
            # 1. 将当前登录用户的权限信息查询出来
            # user_obj.roles.all()  # QuerySet
            queryset = user_obj.roles.all().filter(permissions__isnull=False).values(
                'permissions__url',
                'permissions__title',
                'permissions__is_menu',
                'permissions__icon',
            ).distinct()
            print(queryset)
            # [{'permissions__url': '/customer/list/', 'permissions__title': '客户列表', 'permissions__is_menu': True, 'permissions__icon': 'fa-ravelry'}, {'permissions__url': '/customer/add/', 'permissions__title': '添加客户', 'permissions__is_menu': True, 'permissions__icon': 'fa-bed'}]
            print('-0-' * 120)
            # 先取到权限列表
            permission_list = []
            # 存放菜单信息的列表
            menu_list = []

            for i in queryset:
                permission_list.append(i['permissions__url'])  # 能够访问的权限列表
                # 再取出菜单列表
                if i.get('permissions__is_menu'):
                    menu_list.append({
                        'url': i['permissions__url'],  # 菜单的URL
                        'title': i['permissions__title'],  # 菜单的标题
                        'icon': i['permissions__icon']  # 菜单的图标样式
                    })
            print('获取权限信息:', permission_list)
            print('获取菜单信息:', menu_list)

            # 2. 将权限信息保存到session数据中
            permission_key = getattr(settings, 'PERMISSION_SESSION_KEY', 'permission_list')
            menu_key = getattr(settings, 'MENU_SESSION_KEY', 'menu_list')
            request.session[permission_key] = permission_list
            # 3. 存菜单信息到session数据中
            request.session[menu_key] = menu_list
            return redirect('/customer/list/')
        else:
            error_msg = '用户名或密码错误'
    return render(request, 'login.html', {'error_msg': error_msg})


def logout(request):
    request.session.flush()
    return redirect('/login/')

 如下图中:李庆阳登录上后,他其实是没有帐单管理的权限,但图2页面中还显示帐单管理菜单栏。

   

  那我这块图2中菜单不是写死的,应该从当前这次请求的session数据中拿到它的菜单信息(menu_list),把这个列表拿出来循环它,把它中的每一项做成菜单样展示出来。

 (2)web/templates/layout.html布局模版中:

  把菜单栏的两个a标签改成动态生成a标签即可--从模版中能拿到request(因为render函数第一参数就是request),--request.session就能拿到session,拿到session就能拿到视图中之前存的值menu_list

如下图中,王帅登录上去后能看到自己的菜单栏:

            <div class="static-menu">
                {% for menu in request.session.menu_list %}
                    <a href="{{ menu.url }}" class="active"><span class="icon-wrap"><i class="fa {{ menu.icon }}"></i></span> {{ menu.title }} </a>
                {% endfor %}
            </div>

二.动态菜单问题

 上述效果中,问题1::菜单栏中几个菜单都有active样式--正常的话应该是鼠标滑过是才是蓝色active-->访问的当前的url和菜单的url匹配上后才加active样式--模版中做判断---layout.html中改成如下代码即可解决。

 <a href="{{ menu.url }}" class="{% if request.path_info == menu.url %}active{% endif %}"><span class="icon-wrap"><i class="fa {{ menu.icon }}"></i></span> {{ menu.title }} </a>

  问题2:在模版语言中把session key写死了(settings.py配置文件是写死的):request.session.menu_list写死了,这样写成立的条件是session中的key必须叫menu_list,如果我改成别的,那模版代码就跑不了了---> 模版中<div class="static-menu">...</div>这段代码其实我在后端既能生成它又能用到数据--->自定义inclusion_tag(在模版语言中碰到这种场景:返回一段html代码,同时html代码中还用到变量(数据)就可用此法实现).---inclusion_tag就是一局部的render函数--把一些数据填充/渲染到html代码上.

settings.py中:

# 权限组件的相关配置
WHITE_URLS = [
'/login/',
'/logout/',
'/reg/',
'/admin/.*',
]
PERMISSION_SESSION_KEY = 'permission_url'
MENU_SESSION_KEY = 'menu_list'

自定义inclusion_tag顺序:

(1)web/templatetags包/rbac.py:

  导入template,生成一动态实例,定义一个方法(传request)

from django import template
from django.conf import settings
import re

#生成一动态实例
register = template.Library()
#menu.html中放的就是一小段html代码
@register.inclusion_tag(filename='menu.html')
#此函数功能就是把它自己返回的结果去填充menu.html这个页面(menu.html中有特殊符号)
def show_menu(request):
    # 1先从配置文件中找到存放菜单信息的session key是什么
    menu_key = getattr(settings, 'MENU_SESSION_KEY', 'menu_list')
    # 2从session中取出菜单信息
    menu_list = request.session[menu_key]
    # 给当前的菜单添加active样式
    for menu in menu_list:
        if re.match(r'^{}$'.format(menu['url']), request.path_info):
            # 当前这个menu需要加一个active样式
            menu['class'] = 'active'
            break
    return {'menu_list': menu_list}

(2)web/tempaltes/menu.html中:

  放的就是layout.html中的上述那小段html代码剪贴过来--它就用到一request参数

<div class="static-menu">
    {% for menu in menu_list %}
        <a href="{{ menu.url }}" class="{{ menu.class }}"><span
                class="icon-wrap"><i class="fa {{ menu.icon }}"></i></span> {{ menu.title }} </a>
    {% endfor %}
</div>

(3)web/templates/layout.html中使用自定的inclusion_tag:

   从被剪贴的位置load一下rbac文件名

  传入你rbac.py中的方法并传参。

 三.RBAC组件封装

  到此为止权限系统写完了,但若把它很快嵌入到任意项目中还是不能,因为它的各小功能在此项目中各app中都写了,从rbac/views.py中可知,权限信息的加载是在login视图这一步做成的,那若嵌入到crm项目就不行(因为crm项目中也写了login登录视图)。

1.第一步封装加载权限信息和菜单信息的代码到一单独模块

 rbac/views.py中login视图中有两部分:第一部分是登录成功功能,第二部分是加载权限信息与判断功能。--那我把第二部分代码单独拿出来即可.

(1)rbac/utils包/permission.py中:

  定义init方法初始化权限的功能:传request,传已经登录的用户对象,

"""
RBAC权限相关后端模块
"""
from django.conf import settings
def init(request,user_obj):
    """
    根据当前登录的用户初始化权限信息和菜单信息保存到session
    :param request:请求对象
    :param user_obj:登录的用户对象
    :return:
    """
    # 1. 将当前登录用户的权限信息查询出来
    # user_obj.roles.all()  # QuerySet
    queryset = user_obj.roles.all().filter(permissions__isnull=False).values(
        'permissions__url',
        'permissions__title',
        'permissions__is_menu',
        'permissions__icon',
    ).distinct()
    # 先取到权限列表
    permission_list = []
    # 存放菜单信息的列表
    menu_list = []

    for i in queryset:
        permission_list.append(i['permissions__url'])  # 能够访问的权限列表
        # 再取出菜单列表
        if i.get('permissions__is_menu'):
            menu_list.append({
                'url': i['permissions__url'],  # 菜单的URL
                'title': i['permissions__title'],  # 菜单的标题
                'icon': i['permissions__icon']  # 菜单的图标样式
            })

    # 2. 将权限信息保存到session数据中
    permission_key = getattr(settings, 'PERMISSION_SESSION_KEY', 'permission_list')
    menu_key = getattr(settings, 'MENU_SESSION_KEY', 'menu_list')
    request.session[permission_key] = permission_list
    # 3. 存菜单信息到session数据中
    request.session[menu_key] = menu_list

(2)rbac/views.py中:引用它
  导入它,并引用它且传参.

2.第二步把菜单生成inclusion_tag放到rbac app下面:(我此前是写在web app下面了)

  把web/templatetag包剪贴到rbac应用中.

  把web/templates/menu.html拉到rbac/templates/中.

   

 

   那由上图2中可看出rbac/templates/中有rbac写的一登录页面和一小截html代码的menu.html文件,

 新建rbac/templates/rbac文件夹专放权限相关的,所以把menu.html移到此文件夹下--此时注意在rbac.py中要给它指路-----@register.inclusion_tag(filename='rbac/menu.html')

  menu.py中:用到样式文件,而这些样式文件是从web/templates/layout.html中定义的,所以把layout.html中生成样式的代码拷贝出来,因为这些样式只是在rbac权限组件中生成菜单样式中用上的,新建rbac app自己的静态目录和文件:rbac/static目录/css目录/rbac_menu.css文件中:

settings.py中:
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'static')
]
那此时我就可直接把rbac应用直接复制到别的项目中就可用了

 四.RBAC权限组件使用指南---把它应用到我此前的crm项目中

  登录crm项目中,菜单栏客户列表--销售角色不能看,而班主作只能看班级列表那我这里就建两角色销售和班主任。

1.复制RBAC app到crm项目中:knight/rbac---注册settings.py中:'rbac.apps.RbacConfig',

2.在settings中配置权限组件相关的配置.

# 权限组件的相关配置
WHITE_URLS = [
'/login/',
'/logout/',
'/reg/',
'/admin/.*',
]
PERMISSION_SESSION_KEY = 'permission_url'
MENU_SESSION_KEY = 'menu_list'

3.修改表结构,创建表结构:rbac/models.py:

from crm.models import UserProfile

  我的权限组件models.py中是自己写的用户表即我的登录是在rbac里做的,那现在我用到crm项目中了,它有自己的一套登录注册--基于auth做的,

  删除用户表,且角色表得去关联crm的用户表

  先清空开发阶段的变更记录:

 

 

 

   删除admin.py中UserInfor注册。

python manage.py makemigrations  ---把你的models.py中的变更记录

python manage.py migrate

 4.录入权限信息

  登录http://127.0.0.1:8000/admin/

             

     

 

5.在登录业务逻辑登录成功后面加入执行权限初始化(把权限信息在登录时就加载到session中,后面才能做判断)crm/views.py中:from rbac.utils import permission

 

 6.注册中间件实现权限的校验--settings.py中: 'rbac.middleware.RBACMiddleware',  (rbac应用下的模块下的类)

7.使用菜单inclusion_tag  ---base.html中:

  我crm中的菜单是在base.html模版中生成的,且它的菜单列表是li标签且套在ul标签中,而我rbac中的menu.html是直接写成a标签所以不能直接用。所以得在它的a标签上也套个li标签,且把它div换成ul标签妈可如下

 

 base.html中改成如下:

 

 然后进入登录页面用李三三用户登录(他是销售角色没班级):

 

posted on 2020-04-21 16:04  单爆手  阅读(454)  评论(1)    收藏  举报