• Django之基于RBAC权限控制生成动态菜单


    一级菜单

    1.model设计

    from django.db import models            
    class Permission(models.Model):
        url = models.CharField(max_length=64, verbose_name='权限')
        title = models.CharField(max_length=32, verbose_name='标题')  # 备注这个url是做什么的
        icon = models.CharField(max_length=64, null=True, blank=True,verbose_name='图标') # 存放菜单前面的图标
        is_menu = models.BooleanField(default=False) # 用来判断(此url)是否是菜单
                
        def __str__(self):
            return self.title
                
        class Meta:
            verbose_name_plural = '权限管理'
                
                
    class Role(models.Model):
        name = models.CharField(max_length=32, verbose_name='角色')
        permissions = models.ManyToManyField(Permission, verbose_name='角色拥有的权限', blank=True)
                
        def __str__(self):
            return self.name
                
        class Meta:
            verbose_name_plural = '角色管理'
                
                
    class User(models.Model):
        username = models.CharField(max_length=32, verbose_name='用户名',unique=True)
        password = models.CharField(max_length=32, verbose_name='密码')
        roles = models.ManyToManyField(Role, verbose_name='用户的角色', blank=True)
                
        def __str__(self):
            return self.username
                
        class Meta:
            verbose_name_plural = '用户管理'
    models.py

    注意:数据迁移后,去admin后台界面,设置菜单

    2. views.py文件 登录函数中登陆成功后获取当前用户的权限的所有信息(url,title,icon,is_menu)

    如:

    per = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
                                                                        'permissions__title',
                                                                        'permissions__icon',
                                                                        'permissions__is_menu',
                                                                        ).distinct()
    print(per)
    
    ## 打印per的数据
    QuerySet[{
                 'permissions__url': '/customer/list/',
                 'permissions__title': '客户列表',
                 'permissions__icon': 'fa-connectdevelop',
                 'permissions__is_menu': True},
             {
                 'permissions__url': '/customer/add/',
                 'permissions__title': '添加客户',
                 'permissions__icon': None,
                 'permissions__is_menu': False},
             {
                 'permissions__url': '/customer/edit/(?P<cid>\d+)/',
                 'permissions__title': '编辑客户',
                 'permissions__icon': None,
                 'permissions__is_menu': False},
             {
                 'permissions__url': '/customer/del/(?P<cid>\d+)/',
                 'permissions__title': '删除客户',
                 'permissions__icon': None,
                 'permissions__is_menu': False},
             {
                 'permissions__url': '/payment/list/',
                 'permissions__title': '账单列表',
                 'permissions__icon': 'fa-code-fork',
                 'permissions__is_menu': True},
             {
                 'permissions__url': '/payment/add/',
                 'permissions__title': '添加账单',
                 'permissions__icon': None,
                 'permissions__is_menu': False},
             {
                 'permissions__url': '/payment/edit/(?P<pid>\d+)/',
                 'permissions__title': '编辑账单',
                 'permissions__icon': None,
                 'permissions__is_menu': False},
             {
                 'permissions__url': '/payment/del/(?P<pid>\d+)/',
                 'permissions__title': '删除账单',
                 'permissions__icon': None,
                 'permissions__is_menu': False
             }]
    views.py

    3、从上述中的数据中挑出菜单列表

    # 菜单列表
                menu = []
        
                # 权限列表
                permission = []
        
                for i in per:
                    permission.append({
                        'url': i.get('permissions__url')
                    })
                    if i.get('permissions__is_menu'): # 如果permissions__is_menu为True就是菜单,放到菜单列表中
                        menu.append({
                            'url': i.get('permissions__url'),
                            'title': i.get('permissions__title'),
                            'icon': i.get('permissions__icon')
                        })
    View Code

    4. 为了使两个列表(session中作为值)在session中对应的键的名字可以由用户改变,在名字中设置两个常量

    settings.py文件:

    # 存放权限的session的key
    PERMISSION_SESSION_KEY = 'permission'
    
    # 存放菜单的session的key
    MENU_SESSION_KEY = 'menu'
    View Code

    5. 将两个列表放到session中

    登录函数,第三步后面

    from django.conf import settings
    request.session[settings.PERMISSION_SESSION_KEY] = permission
    request.session[settings.MENU_SESSION_KEY] = menu
    View Code

    6. 权限验证的中间件中获取session中的PERMISSION_SESSION_KEY进行权限验证

    permission = request.session.get(settings.PERMISSION_SESSION_KEY)
    # 权限验证(url,中间件中获取的当前url地址如:通过request.path_info获取)
    for i in permission:
        if re.match(r'^{}$'.format(i['url']), url):
        return
    View Code

    7. incluesion_tags(模板中调用函数)

    目的:用于前端菜单的展示而编写的函数
    函数作用:为菜单添加样式,然后通过装饰器交给特定的模板,
    具体如下:

    my_tags.py文件

    # -*- coding: utf-8 -*-
    # __author__ = "maple"
                
    from django import template
    from django.conf import settings
    import re
    register = template.Library()
    @register.inclusion_tag('menu_tag.html') # 讲给这个模板进行渲染
    def menu(request):
        url = request.path_info  # 获取当前url地址
        menu_list = request.session[settings.MENU_SESSION_KEY]  # 获取session中的菜单url地址
    
        # 添加激活的样式
        for i in menu_list:
            if re.match(f'^{i["url"]}$',url): # 判断,如果当前页面地址栏中的url地址是菜单中的地址
                i['class'] = 'active'         # 就给菜单中的地址加上active类选择器。
                break    
                return {'menu_list':menu_list}          
                
        #此时,menu_list列表中的数据为:(前端页面当前停留在客户列表菜单上)
        [{
        'url': '/customer/list/',
        'title': '客户列表',
        'icon': 'fa-connectdevelop',
        'class': 'active'},
        {
        'url': '/payment/list/',
        'title': '账单列表',
        'icon': 'fa-code-fork'
        }]
    View Code

    8. menu_tag.html 渲染模板

    <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>
    View Code

    9.将incluesion_tags应用到前端html页面中

    <div class="left-menu">
    <div class="menu-body">
    {% load my_tags %} # my_tags为存放menu函数的文件名,
    {% menu request %} # request为第七步中的menu函数的参数
    </div>
    </div>
    View Code

    注意:tags文件都放在templatetags目录中,目录名字不能变,位置随意。

    二级菜单

    1. models设计

    from django.db import models
    
    
    class Menu(models.Model):
        title = models.CharField(max_length=32, verbose_name='标题')
        icon = models.CharField(max_length=64, null=True, blank=True,verbose_name='图标')
        weight = models.IntegerField(default=0, verbose_name='权重')
    
        def __str__(self):
            return self.title
    
    
    class Permission(models.Model):
        url = models.CharField(max_length=64, verbose_name='权限')
        title = models.CharField(max_length=32, verbose_name='标题')
        menu = models.ForeignKey(Menu, null=True, blank=True,verbose_name='菜单')
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name_plural = '权限管理'
    
    
    class Role(models.Model):
        name = models.CharField(max_length=32, verbose_name='角色')
        permissions = models.ManyToManyField(Permission, verbose_name='角色拥有的权限', blank=True)
    
        def __str__(self):
            return self.name
    
        class Meta:
            verbose_name_plural = '角色管理'
    
    
    class User(models.Model):
        username = models.CharField(max_length=32, verbose_name='用户名', unique=True)
        password = models.CharField(max_length=32, verbose_name='密码')
        roles = models.ManyToManyField(Role, verbose_name='用户的角色', blank=True)
    
        def __str__(self):
            return self.username
    
        class Meta:
            verbose_name_plural = '用户管理'
    models.py

    2. settings中配置seesion中的键的名字,让key更灵活

    # 存放权限的session的key
    PERMISSION_SESSION_KEY = 'permission'
    
    # 存放菜单的session的key
    MENU_SESSION_KEY = 'menu'
    settings.py

    3. 用户登录后,获取登录用户的权限以及菜单信息,并放入session中

    def login(request):
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            obj = models.User.objects.filter(username=username, password=password).first()
            if not obj:
                error = '用户名或密码错误'
                return render(request, 'login.html', locals())
    
            # 获取权限的信息
            per = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
                                                                                'permissions__title',
                                                                                'permissions__menu__title',
                                                                                'permissions__menu__icon',
                                                                                'permissions__menu__id',
                                                                                ).distinct()
            #-----------------------------------------------------------------
            # 菜单列表(后面转列表)
            dic = {}
            # 权限列表
            permission = []
            for i in list(per):
                # 获取当前用户的权限列表
                print(i, type(i))
                permission.append({
                    'url': i.get('permissions__url')
                })
                # 获取当前菜单列表
                if i['permissions__menu__id']:
                    if dic.get(i['permissions__menu__id']):
                        dic[i['permissions__menu__id']]['children'].append(
                            {'url': i['permissions__url'], 'title': i['permissions__title']})
                    else:
                        dic[i['permissions__menu__id']] = {'title': i['permissions__menu__title'],
                                                           'icon': i['permissions__menu__icon'],
                                                           'children': [
                                                               {'url': i['permissions__url'],
                                                                'title': i['permissions__title']}
                                                           ]
                                                           }
    
            request.session[settings.PERMISSION_SESSION_KEY] = permission
            request.session[settings.MENU_SESSION_KEY] = dic
            #-----------------------------------------------------------------
            request.session['is_login'] = 1
            return redirect('index')
        return render(request, 'login.html', locals())
    views.py

    4. 中间件中提取权限列表

    # -*- coding: utf-8 -*-
    # __author__ = "maple"
    from django.utils.deprecation import MiddlewareMixin
    from django.conf import settings
    from django.shortcuts import redirect, HttpResponse
    import re
    
    
    class AuthMiddleware(MiddlewareMixin):
        def process_request(self, request):
            url = request.path_info
            # 白名单 :登陆、注册、admin
            for i in settings.WHITE_LIST:
                if re.match(i, url):
                    return
    
            # 登陆验证
            is_login = request.session.get('is_login')
            if is_login != 1:
                return redirect('login')
    
            # 免认证 登录之后所有人都能访问的地址
            for i in settings.PUBLIC_LIST:
                if re.match(i, url):
                    return
    
            # 权限验证
            permission = request.session.get(settings.PERMISSION_SESSION_KEY)
            for i in permission:
                if re.match(r'^{}$'.format(i['url']), url):
                    return
            return HttpResponse('没有权限,请联系管理员!')
    中间件.py

    5.自定义标签获取菜单信息,通过函数,动态生成二级菜单

    # -*- coding: utf-8 -*-
    # __author__ = "maple"
    from django import template
    from django.conf import settings
    import re
    
    register = template.Library()
    
    @register.inclusion_tag('menu_tag.html')
    def menu(request):
        url = request.path_info
        menu_list = request.session[settings.MENU_SESSION_KEY]
    
        # 添加激活的样式
        for k, v in menu_list.items():
            print(k,v)
            """
            v ={'title': '客户管理', 
            'icon': 'fa-connectdevelop', 
            'children': [{'url': '/customer/list/', 'title': '客户列表'}]}
            """
            for i in v["children"]:
                if re.match(r'^{}$'.format(i["url"]), url):
                    i["class"] = 'active'
                    break
        return {'menu_list': menu_list}
    my_tags.py
    <div class="static-menu">
        {% for one_menu_id,two_menu in menu_list.items %}
            <a href="" >
                <span class="icon-wrap"><i class="fa {{ two_menu.icon }}"></i></span> {{ two_menu.title }}</a>
            {% for child in two_menu.children %}
                <a href="{{ child.url }}" class="{{ child.class }}">
                    <span class="icon-wrap"><i class="fa {{ child.icon }}"></i></span> {{ child.title }}</a>
            {% endfor %}
        {% endfor %}
    </div>
    menu_tag.html

    7.应用自定义标签到项目中

        <div class="left-menu">
            <div class="menu-body">
                {% load my_tags %}
                {% menu request %}
            </div>
        </div>
    View Code

    8.使用jQuery实现隐藏二级菜单,点击一级菜单,出现二级菜单

    略....................

    目录分布

     

     

  • 相关阅读:
    HDOJ 2689
    UVALive 3635 Pie 切糕大师 二分
    黑马程序员 Java基础<十八>---> 网路编程
    C# 数据库dataGridView刷新数据和主外键判断
    影视-纪录片:《生死洄游》
    汉语-词语:旅行
    汉语-词语:探险
    风水学:龙脉
    人物-探险家:斯文·赫定
    影视-纪录片:《河西走廊之嘉峪关》
  • 原文地址:https://www.cnblogs.com/kindvampire/p/12173299.html
Copyright © 2020-2023  润新知