如何制作一个简单的django权限组件

Posted 春秋羽

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何制作一个简单的django权限组件相关的知识,希望对你有一定的参考价值。

第一步:

  新建一个app

第二步:目录结构

           

  第三步:代码如下

第一步:写models

from django.db import models


# 定义一级菜单
class Menu(models.Model):
    title = models.CharField(max_length=32, unique=True)  # 名称
    icon = models.CharField(max_length=32, null=True, blank=True)  # 图标代码:可以为空   

    def __str__(self):
        return self.title


# 定义权限
class Permission(models.Model):
    """
    权限表
    """
    title = models.CharField(verbose_name=\'标题\', max_length=32)  # 定义标题,动态生成时候,要用到
    url = models.CharField(verbose_name=\'含正则的URL\', max_length=128)  # url地址
    name = models.CharField(max_length=32, verbose_name=\'url别名\', unique=True)  # 每个url的别名,唯一,且不能为空
    menu = models.ForeignKey(\'Menu\', null=True, blank=True, verbose_name=\'一级菜单\')  # 关联的一级菜单
    prent = models.ForeignKey(\'Permission\', null=True, blank=True, verbose_name=\'关联列表\')  # 自己关联自己中的某个列表要使用的

    def __str__(self):
        return self.title


class Role(models.Model):
    """
    角色
    """
    title = models.CharField(verbose_name=\'角色名称\', max_length=32)  # 定义角色的名字
    permissions = models.ManyToManyField(verbose_name=\'拥有的所有权限\', to=\'Permission\', blank=True)

    # 和权限多对多关系
    def __str__(self):
        return self.title


# 定义用户
class UserInfo(models.Model):
    """
    用户表
    """
    name = models.CharField(verbose_name=\'用户名\', max_length=32)  # 账号
    password = models.CharField(verbose_name=\'密码\', max_length=64)  # 密码
    email = models.CharField(verbose_name=\'邮箱\', max_length=32)  # 邮箱
    roles = models.ManyToManyField(verbose_name=\'拥有的所有角色\', to=\'Role\', blank=True)  # 多对多关联角色表

    def __str__(self):
        return self.name
models.py文件中内容

第二步:写server文件下的init_permission.py

from django.conf import settings



# 获取当前登录用户的所有权限
def init_permission(request, user):
    rbac_list = user.roles.filter(permissions__url__isnull=False).values(
        \'permissions__id\', # 获取权限的id
        \'permissions__url\',# 获取权限的url
        \'permissions__title\',# 获取权限的名字
        \'permissions__name\',# 获取权限的别名
        \'permissions__prent_id\',# 获取关联列表的id
        \'permissions__prent__name\',# 获取关联列表的别名
        \'permissions__menu_id\',# 获取一级菜单的id
        \'permissions__menu__title\',# 获取一级菜单的名字
        \'permissions__menu__icon\',  # 获取一级菜单的图标代码
    ).distinct() # 去重

    url_dict = {}  # 该用户所有的权限
    menu_dic = {}  # 根据权限生成的菜单
    for i in rbac_list: # 循环所有筛选出来的内容
        """
        用每个权限的别名做key,
        value是[
            \'url\':权限的url,
            \'id\':每个权限的id,
            \'pid\':\'它所关联权限的id\',
            \'title\':\'它自己的标题\',
            \'pname\':\'它所关联权限的别名\'
            ]

        """
        url_dict[i[\'permissions__name\']] = {\'url\': i[\'permissions__url\'], 
                                            \'id\':i[\'permissions__id\'],
                                            \'pid\':i[\'permissions__prent_id\'],
                                            \'title\':i[\'permissions__title\'],
                                            \'pname\':i[\'permissions__prent__name\'],
                                            }
        # 获取一级菜单的id
        menu_id = i.get(\'permissions__menu_id\')
        # 判断如果没有一级菜单的id就重新循环
        if not menu_id:
            continue
        # 判断一级菜单的id是否存在menu_dic这个字典里
        # 如果存在
        if menu_id not in menu_dic:
            """
            结构:
            一级菜单id = {
            \'title\':一级菜单的名称
            \'icon\':一级菜单的图标代码
            \'children\':[{
                
                \'title\':二级菜单的标题,
                \'url\':二级菜单的url
                \'id\':二级菜单的id
    
                }]}
                
            """
            menu_dic[i[\'permissions__menu_id\']] = {
                \'title\': i[\'permissions__menu__title\'],
                \'icon\': i[\'permissions__menu__icon\'],
                \'children\': [
                    {\'title\': i[\'permissions__title\'],
                     \'url\': i[\'permissions__url\'],
                     \'id\':i[\'permissions__id\']
                     }
                ]
            }
        else:
            menu_dic[i[\'permissions__menu_id\']][\'children\'].append({\'title\': i[\'permissions__title\'],
                                                                    \'url\': i[\'permissions__url\'],
                                                                    \'id\': i[\'permissions_id\']
                                                                    })
    
    # 把筛选出来的该用户所有权限和菜单结构全部存入到session中
    # settings.PERMISSION_SESSION_KEY和settings.MENU_SESSION_KEY:自己在自己的settings文件中设置
    request.session[settings.PERMISSION_SESSION_KEY] = url_dict
    request.session[settings.MENU_SESSION_KEY] = menu_dic
权限相关的所有代码

第三步:写middlewear下的rbac.py

import re
from django.conf import settings
from django.shortcuts import HttpResponse
from django.utils.deprecation import MiddlewareMixin




# 中间件
class RbacPermissionMiddleware(MiddlewareMixin):
    
    def process_request(self, request):
        # 获取当前访问的地址
        current_url = request.path_info
        # 循环判断白名单,自己在settings设置自己的白名单
        for i in settings.WITER_LIST:
            if re.match(\'^%s$\'%i, current_url):
                return
        # 获取当前用户权限
        permissions_dict = request.session[settings.PERMISSION_SESSION_KEY]
        # 小菜单要使用
        request.breadcrumb_list=[{\'title\':\'首页\',\'url\':\'/\'}]
        # 循环判断
        flag = False
        # 循环用户的所有权限
        for item in permissions_dict.values():
            # 正则匹配
            if re.match(item[\'url\'], current_url):
                # 匹配成功修改flag
                flag = True
                # 获取id,父级id,父级url别名
                id = item[\'id\']
                pid = item[\'pid\']
                pname = item[\'pname\']
                # 判断是否有父级id
                if pid:
                    # 获取父级id,在动态生成菜单时要用
                    request.current_menu_id = pid
                    # 父级的url,title,和自己的url好和title
                    request.breadcrumb_list.extend([
                        {\'url\':permissions_dict[pname][\'url\'],\'title\':permissions_dict[pname][\'title\']},
                        {\'url\':item[\'url\'],\'title\':item[\'title\']},
                                                    ])
                else:
                    # 获取自己id,在动态生成菜单时要用
                    request.current_menu_id = id
                    # 自己的url好和title
                    request.breadcrumb_list.extend([
                        {\'url\': item[\'url\'], \'title\': item[\'title\']}
                    ])
                break
        # 判断当前用户是否有权限访问这个页面
        if not flag:
            return HttpResponse(\'你没有此权限\')
中间件的代码

第四步:写templatetags下的rbac.py

from django.conf import settings
from django import template
from collections import OrderedDict

register = template.Library()


# 设置左侧菜单的模板组件
@register.inclusion_tag(\'rbac/module.html\')
def show_results(request):
    # 获取存储菜单的字典
    menu_dic = request.session[settings.MENU_SESSION_KEY]
    # 让字典变的有序,python3.6在一些特殊的情况下会变得无序
    order_dict = OrderedDict()
    # 循环这个字典,字典先排序
    for item in sorted(menu_dic):
        # 把这个字典里所有内容传入到有序字典里
        order_dict[item] = menu_dic[item]
        # 给所有的一级菜单中都加入一个hide(隐藏)
        order_dict[item][\'class\'] = \'hide\'
        # 循环这个字典的二级菜单
        for i in order_dict[item][\'children\']:
            # 判断当前访问的url的id是否是当前循环二级菜单的id
            if request.current_menu_id == i[\'id\']:
                # 如果是的话,给他添加\'active\'和去除一级菜单的\'hide\'
                i[\'class\'] = \'active\'
                order_dict[item][\'class\'] = \'\'
    # 把字典出入html文件里
    return {\'menu_dic\': order_dict}


# 设置小菜单
@register.inclusion_tag(\'rbac/breadcrumb.html\')
def breadcrumb(request):
    # 把小菜单需要的列表传入
    return {\'breadcrumb_list\': request.breadcrumb_list}


# 设置过滤器
@register.filter
def has_permission(request, name):
    # 判断传入的url别名是否在菜单字典中
    if name in request.session.get(settings.PERMISSION_SESSION_KEY):
        return True
标签代码
<div class="multi-menu">
    {% for item in menu_dic.values %}
         <div class="item">
             <div class="title">
                 <span class="icon-wrap"><i class="fa {{ item.icon }}"></i></span>{{ item.title }}
             </div>
             <div class="body {{ item.class }}">
                 {% for per in item.children %}
                    <a href="{{ per.url }}" class="{{ per.class }}" >{{ per.title }}</a>
                 {% endfor %}
             </div>
         </div>
    {% endfor %}
</div>
module.html的代码
<ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;">
    {% for item in breadcrumb_list %}
        {% if forloop.last %}
            <li class="active">{{ item.title }}</li>
            {% else %}
                <li><a href="{{ item.url }}">{{ item.title }}</a></li>
        {% endif %}
    {% endfor %}
</ol>
breadcrumb.html的代码

第五步:admin.py中的代码

from django.contrib import admin
from rbac import models


class PermissionAdmin(admin.ModelAdmin):
    list_display = [\'title\', \'url\',\'menu\',\'prent\',\'name\'] # 页面要显示的内容
    list_editable = [\'url\',\'menu\',\'prent\',\'name\']   # 可以在页面更改的内容



admin.site.register(models.Role)
admin.site.register(models.Permission, PermissionAdmin)
admin.site.register(models.UserInfo)
admin.site.register(models.Menu)
admin.py中的代码

有一些注释可能写的比较迷,直接贴图吧

左菜单/大菜单

小菜单

简单的组件写完了,组件的css和js代码根据自己设计的页面来写

 

 

组件的使用方法

 

先设置settings文件
INSTALLED_APPS下添加 \'rbac.apps.RbacConfig\'
MIDDLEWARE下添加\'rbac.middleware.rbac.RbacPermissionMiddleware\'
PERMISSION_SESSION_KEY = \'permission_dict\' # 设置存储当前用户可以访问的地址的key
MENU_SESSION_KEY=\'menu_dict\' # 存储菜单相关的列表的key
WITER_LIST = [\'^/login/$\', # 白名单
\'^/reg/$\',
\'^/admin/.*$\']
在自己的view中使用要导入
from rbac.service.init_permission import init_permission
当登录成功
import init_permission(request,当前用户的对象)

以上是关于如何制作一个简单的django权限组件的主要内容,如果未能解决你的问题,请参考以下文章

如何使用导航组件处理片段内的向上按钮

Django REST框架--认证和权限

Django之路- 如何开发通用且万能的的权限框架组件

Django之路- 如何开发通用且万能的的权限框架组件

django之权限组件

django(权限认证)系统——第三方组件实现Object级别权限控制