django-权限管理(day89)

创建权限表

基于角色的权限管理,一个员工可以有多个角色,每个角色的访问权限以url的方式存储。
基于角色的权限管理

建表

from django.db import models


class User(models.Model):
    """
    用户表
    """
    username = models.CharField(verbose_name='用户名', max_length=32)
    password = models.CharField(verbose_name='密码', max_length=64)
    email = models.EmailField(verbose_name='邮箱')

    def __str__(self):
        return self.username


class Role(models.Model):
    """
    角色表
    """
    caption = models.CharField(verbose_name='角色', max_length=32)

    def __str__(self):
        return self.caption


class User2Role(models.Model):
    """
    用户角色关系表
    """
    user = models.ForeignKey(User, verbose_name='用户', related_name='roles')
    role = models.ForeignKey(Role, verbose_name='角色', related_name='users')

    def __str__(self):
        return '%s-%s' % (self.user.username, self.role.caption,)


class Menu(models.Model):
    """
    菜单表
    """
    caption = models.CharField(verbose_name='菜单名称', max_length=32)
    parent = models.ForeignKey('self', verbose_name='父菜单', related_name='p', null=True, blank=True)

    def __str__(self):
        prev = ""
        parent = self.parent
        while True:
            if parent:
                prev = prev + '-' + str(parent.caption)
                parent = parent.parent
            else:
                break
        return '%s-%s' % (prev, self.caption,)


class Permission(models.Model):
    """
    权限
    """
    caption = models.CharField(verbose_name='权限', max_length=32)
    url = models.CharField(verbose_name='URL正则', max_length=128)
    menu = models.ForeignKey(Menu, verbose_name='所属菜单', related_name='permissions',null=True,blank=True)

    def __str__(self):
        return "%s-%s" % (self.caption, self.url,)


class Action(models.Model):
    """
    操作:增删改查
    """
    caption = models.CharField(verbose_name='操作标题', max_length=32)
    code = models.CharField(verbose_name='方法', max_length=32)

    def __str__(self):
        return self.caption


class Permission2Action2Role(models.Model):
    """
    权限操作关系表
    """
    permission = models.ForeignKey(Permission, verbose_name='权限URL', related_name='actions')
    action = models.ForeignKey(Action, verbose_name='操作', related_name='permissions')
    role = models.ForeignKey(Role, verbose_name='角色', related_name='p2as')

    class Meta:
        unique_together = (
            ('permission', 'action', 'role'),
        )

    def __str__(self):
        return "%s-%s-%s" % (self.permission, self.action, self.role,)

models查询权限

def permit(request):
    """
    权限管理,获取权限,并存放在session中
    :param request:
    :return:
    """
    from django.db.models import Count

    user_obj = models.User.objects.filter(username="zouruncheng").first()
    print(user_obj)

    # 获取 user2role信息
    x = models.User2Role.objects.filter(user_id=user_obj.id)


    # 通过users关联到User2Role表,通过user__关联到user表。获取当前用户的角色
    role_obj = models.Role.objects.filter(users__user__username="zouruncheng").all()
    print(role_obj)

    # 一个用户可能有多个角色,查询出的数据可能重复。annotate分组问题?
    # permission_list = models.Permission2Action2Role.objects.filter(role__in=role_obj).
    #     values("permission__url","action__code").annotate(c=Count("id"))

    # 使用distinct时效率较低,不会增加额外的列
    permission_list = models.Permission2Action2Role.objects.filter(role__in=role_obj).
        values("permission__url","action__code").distinct()

    print(permission_list)

    # 构造权限条件
    permission_dict = {}
    for item in permission_list:
        if item["permission__url"] in permission_dict:
            permission_dict[item["permission__url"]].append(item["action__code"])
        else:
            permission_dict[item["permission__url"]]=[item["action__code"]]
    print(permission_dict)
    # {'/order.html': ['post', 'get', 'edit', 'del'], '/users.html': ['post', 'get', 'edit', 'del']}

    request.session["permission_dict"] = permission_dict
    return HttpResponse("模拟登录成功")

模拟权限访问

1.建立一个关于权限访问的中间件
MIDDLEWARE = [
    'middleware.permission.PermissionMiddle',
]

2.中间件

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class PermissionMiddle(MiddlewareMixin):
    """
    权限过滤,中间件
    """
    def process_request(self,request,*args,**kwargs):

        # 只有授权才能访问的url
        invalid_url = ["/permit/order.html"]

        if request.path_info in invalid_url:
            action = request.GET.get("md")  # GET传参---?md=get
            permission_dic = request.session.get("permission_dict")
            # {
            #   '/order.html': ['post', 'get', 'edit', 'del'],
            #   '/users.html': ['post', 'get', 'edit', 'del']
            # }
            if not permission_dic:
                return HttpResponse("无权限(未登录)")

            action_list = permission_dic.get(request.path_info)
            if not action_list:
                return HttpResponse("无权限2")

            if action not in action_list:
                return HttpResponse("无权限3")

生成权限菜单

# ==========================day90===============================================
# 根据表结构,生成权限菜单
def menu(request):
    all_menu_list = models.Menu.objects.values("id", "caption", "parent_id")
    # print(all_menu_list)

    # user = models.User.objects.filter(rusername="zouruncheng")
    role_list = models.Role.objects.filter(users__user__username="zouruncheng").all()
    permission_list = models.Permission2Action2Role.objects.filter(role__in=role_list).
        values("permission__id", "permission__url", "permission__caption", "permission__menu__id").distinct()
    # print(permission_list)
    # print(permission_list.count())

    # 将权限挂靠到菜单上
    all_menu_dict = {}
    for row in all_menu_list:
        row["child"] = []
        row["status"] = False  # 是否显示该权限
        row["opened"] = False  # 菜单是否展开
        all_menu_dict[row["id"]]=row

    for per in permission_list:
        if not per["permission__menu__id"]:
            continue

        item = {
            "id":per["permission__id"],
            "caption":per["permission__caption"],
            "url":per["permission__url"],
            "parent_id":per["permission__menu__id"],
            "status":True,
            "opened":False
        }

        # if re.match("permission__url",request.path_info):
        if re.match(item["url"], "/permit/users.html"):
            item["opened"]=True

        pid = item["parent_id"]

        all_menu_dict[pid]["child"].append(item)

        # 将当前权限的父级status=True
        temp = pid
        while not all_menu_dict[temp]["status"]:
            all_menu_dict[temp]["status"]=True
            temp = all_menu_dict[temp]["parent_id"]
            if not temp:
                break

        # 将当前权限的父级opened = True
        if item["opened"]:
            temp1 = pid
            while not all_menu_dict[temp1]["opened"]:
                all_menu_dict[temp1]["opened"] = True
                temp1 = all_menu_dict[temp1]["parent_id"]
                if not temp1:
                    break

    # print(all_menu_dict)
    # print(all_menu_list)
    # 构造菜单和菜单之间的等级关系
    result=[]
    for row in all_menu_list:
        if row["parent_id"]:
            all_menu_dict[row["parent_id"]]["child"].append(row)
        else:
            result.append(row)
    # print(result)

    # for row in result:
    #     print("----",row["caption"],row["status"],row["opened"])
        """
        ---- 用户管理 True
        ---- 订单管理 True
        ---- 博客管理 False
        """
        '''
        结构化处理结果
        result= [
            {'id':1, 'caption':'菜单1', parent_id:None,status:True,opened:True,child:[

                    {'url':'/order.html','caption': '订单管理','id': 1,'opened': True, 'status': True},
                    {'id':4, 'caption':'菜单1-1', parent_id:1,status:False,opened:False,child:[]},]},

            {'id':2, 'caption':'菜单2', parent_id:None,status:True,opened:False,child:[]},

            {'id':3, 'caption':'菜单3', parent_id:None,status:False,opened:False,child:[]},

        ]
        '''
    def menu_tree(menu_list):
        tpl1 = """
        <div class='menu-item'>
            <div class='menu-header'>{0}</div>
            <div class='menu-body {2}'>{1}</div>
        </div>
        """
        tpl2 = """
        <a href='{0}' class='{1}'>{2}</a>
        """

        menu_str = ""
        for menu in menu_list:
            if not menu['status']:
                continue
            # menu: 菜单,权限(url)
            if menu.get('url'):
                # 权限
                menu_str += tpl2.format(menu['url'],'active' if menu['opened'] else "",menu['caption'])
            else:
                # 菜单
                if menu['child']:
                    child_html = menu_tree(menu['child'])
                else:
                    child_html = ""
                menu_str += tpl1.format(menu['caption'], child_html,"" if menu['opened'] else 'hide')

        return menu_str

    menu_html = menu_tree(result)
    print(menu_html)
    return render(request,"ShowMenu.html",{"menu_html":menu_html})

前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>权限显示菜单</title>
    <style>
        .hide {
            display: none;
        }

        .menu-body {
            margin-left: 20px;
        }

        .menu-body a {
            display: block;
        }

        .menu-body a.active {
            color: red;
        }
    </style>

</head>
<body>
{{ menu_html|safe }}
</body>
<script src="/static/plugin/jquery-3.2.1.js"></script>
<script>
    $(function () {

        $('.menu-header').click(function () {
            $(this).next().removeClass('hide').parent().siblings().find('.menu-body').addClass('hide');

        })

    })
</script>
</html>
原文地址:https://www.cnblogs.com/zouruncheng/p/7214131.html