根据权限动态生成二级菜单

数据库models设计:

from django.db import models


"""
一级菜单
"""
class Menu(models.Model):
    title = models.CharField(max_length=32, unique=True)
    icon = models.CharField(max_length=32, verbose_name="图标", null=True, blank=True)

    class Meta:
        verbose_name_plural = "菜单表"
        verbose_name = "菜单表"

    def __str__(self):
        return self.title

"""
有关联Menu表的是可以做二级展示菜单的权限
未关联Menu表的是不展示的权限
"""
class Permission(models.Model):
    """权限表"""
    titlt = models.CharField(max_length=32, verbose_name="功能")
    url = models.CharField(max_length=32, verbose_name="权限")
    menu = models.ForeignKey(to="Menu", null=True, blank=True)

    class Meta:
        verbose_name_plural = "权限表"
        verbose_name = "权限表"

    def __str__(self):
        return self.titlt


class Role(models.Model):
    """角色表"""
    name = models.CharField(max_length=32, verbose_name="角色名")
    permissions = models.ManyToManyField(to="Permission", verbose_name="角色所拥有的权限")

    class Meta:
        verbose_name_plural = "角色表"
        verbose_name = "角色表"

    def __str__(self):
        return self.name


class User(models.Model):
    """用户表"""
    name = models.CharField(max_length=32, verbose_name="用户名")
    password = models.CharField(max_length=32, verbose_name="密码")
    role = models.ManyToManyField(to="Role", verbose_name="用户拥有的角色")

    class Meta:
        verbose_name_plural = "用户表"
        verbose_name = "用户表"

    def __str__(self):
        return self.name

URL:

url(r'^login/$', views.login),  

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

url判断要执行的函数

VIew登录函数:

from django.shortcuts import render, HttpResponse, redirect, reverse
from rbac import models
from django.conf import settings
from rbac.server.init_permission import init_permission


def login(request):
    if request.method == "POST":
        username = request.POST.get("username")
        pwd = request.POST.get("pwd")
        user = models.User.objects.filter(name=username, password=pwd).first()
        if not user:
            error_msg = "用户名或密码错误!"
            return render(request, "login.html", {"error_mag": error_msg})

        # 调用封装的函数
        init_permission(user, request)
        return redirect(reverse("customer"))
    return render(request, "login.html")

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

用户在登录界面输入账号密码之后在login中获取并和数据库中的用户数据进行比对,
如果取到了真实的用户, 则证明用户输入的账号密码正确, 调用封装好的函数进行设置session, 返回重定向到页面

封装好的设置session的函数:

"""
获取权限的函数
封装成组件
"""
from django.conf import settings


def init_permission(user, request):
    permission_list = user.role.filter(permissions__url__isnull=False).values(
        "permissions__url",
        "permissions__titlt",
        "permissions__menu__title",
        "permissions__menu__id",
        "permissions__menu__icon",
    ).distinct()
    # 存放权限信息的列表
    permission_li = []
    # 存放菜单信息的列表
    menu_li = {}
# 单级菜单循环 for i in permission_list: # i 为字典形式 permission_li.append({"url": i["permissions__url"]})
# if i.get("permissions__is_menu"): # menu_li.append({"url": i["permissions__url"], # "icon": i["permissions__icon"], # "titlt": i["permissions__titlt"], # }) # 二级菜单循环 menu_id = i.get("permissions__menu__id") if not menu_id: continue if menu_id not in menu_li: menu_li[menu_id] = { "title": i["permissions__menu__title"], "icon": i["permissions__menu__icon"], "children": [ {"title": i["permissions__titlt"], "url": i["permissions__url"]} ] } else: menu_li[menu_id]["children"].append( {"title": i["permissions__titlt", "url": i["permissions__url"]]} ) # 将权限信息写入session request.session[settings.PERMISSION_SESSION_KEY] = permission_li # 将菜单信息写入session request.session[settings.MENU_SESSION_KEY] = menu_li

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

在数据库中取出需要的数据并去重,
二级菜单循环中获取menu_id是要判断Permission表是否有外键关联Menu表(即是否是二级展示的权限url)

获取session:

from django import template

register = template.Library()

from django.conf import settings
import re

"""
在此函数内获取init_permission初始化方法设置的session值
并且传递个menu.html进行渲染
"""
@register.inclusion_tag("menu.html")
def menu(request):
    menu_list = request.session.get(settings.MENU_SESSION_KEY)

    return {"menu_list": menu_list}

动态页面渲染:

<div class="multi-menu">
    {% for i in menu_list.values %}
        <div class="item">
            <div class="title"> <i class="fa {{ i.icon }}"></i> {{ i.title }}</div>
            <div class="body">
                {% for f in i.children %}
                    <a href="{{ f.url }}">{{ f.title }}</a>
                {% endfor %}
            </div>
        </div>
    {% endfor %}
</div>

中间件判断权限:

from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
from django.shortcuts import render, HttpResponse, redirect, reverse
import re


class Permissionmiddleware(MiddlewareMixin):
    def process_request(self, request):
        # 目的 : 对权限进行校验
        # 获取当前访问的URL
        url = request.path_info
        # 判断访问的URL是否在白名单中
        for i in settings.WHITE_URL_LIST:
            if re.match(i, url):
                return

        # 获取用户所拥有的权限
        permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
        # 将获取的URL和获取的权限进行一致性校验
        for item in permission_list:
            url_p = item["url"]
            if re.match("^{}$".format(url_p), url):
                return
        else:
            return HttpResponse("没有权限!")

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

在web初次发送请求时, 系统会在中间件中判断所请求时用的url是否在设置的白名单中, 如果在正常向下执行, 不在则获取用户的所拥有的权限,
如果url在设置的白名单内, 执行白名单内的函数, 函数内会设置新的session, 当返回响应重定向时, 再次经过中间件, 则判断用户的权限(session和url的一致性校验)


原文地址:https://www.cnblogs.com/dong-/p/9880049.html