Django第10章: 权限管理(递归菜单树)
Posted fqh202
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django第10章: 权限管理(递归菜单树)相关的知识,希望对你有一定的参考价值。
权限四表(重点)
用户登录
- 进入admin后台填充数据;
- 前端利用form表单登录;
- 用户输入登录信息后, 若后端认证通过,则缓存当前用户的所有权限信息
# views.py============================================
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
username = request.POST.get('username', '')
password = request.POST.get('password', '')
print(request.POST)
user_obj = UserInfo.objects.filter(name=username, password=password)
if not user_obj:
return render(request, 'login.html', {'msg': '用户名或密码有误'})
else:
init_permission(request, user_obj)
return redirect('/index.html')
# init_permission.py============================================ 在rbac的app目录下新建文件夹session_service,将权限初始化文件放入其中即可
from ..models import Menu
# 认证通过则走此逻辑函数
def init_permission(request, user_obj):
permission_items = user_obj.values(
'role__permissions__url',
'role__permissions__title',
'role__permissions__menu_id').distinct()
# 结构: [{'role__permissions__url': '/return_goods.html', 'role__permissions__title': '权限6', 'role__permissions__menu_id': 17}, ...]
# 2, 仅包含当前用户有全访问的url列表
permission_url_list = []
# 3, 仅包含当前用户有权限的菜单和权限名称信息
permission_menu_list = []
# 4,取出所有菜单, 注意必须转换成列表类型,否则在存入session时无法序列化
all_menus = list(Menu.objects.values('id', 'caption', 'parent_id'))
# 5.将权限菜单列表整理成[{{'title': '权限6', 'url': '/return_goods.html', 'menu_id': 17}, ...}]
for item in permission_items:
permission_url_list.append(item['role__permissions__url'])
if item['role__permissions__menu_id']:
temp = {'title': item['role__permissions__title'], 'url': item['role__permissions__url'],
'menu_id': item['role__permissions__menu_id']}
permission_menu_list.append(temp)
from django.conf import settings
# 保存当前用户的相关信息
request.session[settings.SESSION_PERMISSION_URL_LIST_KEY] = permission_url_list
request.session[settings.SESSION_PERMISSION_MENU_LIST_KEY] = permission_menu_list
request.session[settings.SESSION_ALL_MENU_KEY] = all_menus
自定义用户验证
- 在项目目录下新建文件夹
md
,用于存放中间件文件my_middlewares.py
; - 在
settings.py
配置文件中的MIDDLEWARE
添加自定自定义的中间件Md1
; - 自定义
md1
的作用是根据用户请求的url
来判断当前用户是否有此权限获取对应的内容
# my_middlewares.py
import re
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect
from django.conf import settings
class Md1(MiddlewareMixin):
def process_request(self, request):
# 1. 排除无需授权的url, 直接跳过后面的代码进入路由映射
for url in settings.AUTHORIZED_URLS:
print(request.path)
match_url = re.match(url, request.path)
if match_url:
return None
# 2. 若用户申请的urk需要授权访问
# 2.1 取出当前用户的所有有权限访问的url
permission_url_list = request.session.get(settings.SESSION_PERMISSION_URL_LIST_KEY, '')
# 2.2 用户未登录则直接跳转至登录页面;
if not permission_url_list:
return redirect(settings.LOGIN_URL)
# 2.3 已经登录用户
flag = False
# permission_urls = ['/return_goods/', '/admin/']
for db_url in permission_url_list:
# 2.3.1 将当前的url和有权限的url逐个匹配
# 注意必须完全匹配,因为权限的url是正则表达式的形式;
match_ulr = re.match(settings.URL_PATTERN.format(db_url), request.path)
if match_ulr:
# 2.3.2 匹配到则直接退出循环
flag = True
break
# 2.4 用户申请访问的url不在用户权限之内,则返回无权访问信息, 否则,直接pass
if not flag:
if settings.DEBUG:
url_html = '</br>'.join(permission_url_list)
return HttpResponse('无权访问: %s' % url_html)
else:
return HttpResponse('<h1>无权访问</h1>')
# settings.py
AUTHORIZED_URLS = [
'/login.html',
'/index.html',
'/admin',
]
递归生成菜单信息
<!DOCTYPE html>
{% load rbac_tags %}
{% load static %}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>主页面</title>
<link rel="stylesheet" href="/static/css/bootstrap.css">
<script src="{% static '/js/jquery-3.2.1.js' %}"></script>
<style>
{% rbac_css %}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row content container-fluid">
<div class="col-md-2 left_menu">
<div class="panel panel-success">
<div class="panel-body">
{% rbac_menu request %}
</div>
</div>
</div>
<div class="col-md-10 right_content">
<p><h1>{{ content }}</h1></p>
</div>
</div>
</div>
</body>
<script>
{% rbac_js %}
</script>
</html>
自定义标签(最难 )
可以直接在前端页面生成菜单信息;
推导过程比较繁琐;
from django import template
import re
from django.conf import settings
from django.utils import safestring
register = template.Library()
# 生成需要的列表[{'id': 16, 'caption': '换货', 'parent_id': none, children:[**], status:**, open: **}, ...]
def process_menu_data(request):
"""数据库取到要展示的菜单的所有信息, 由于结构比较复杂,需要自定定制流程"""
# [{'id': 16, 'caption': '换货', 'parent_id': 19}, ...]
all_menu_list = request.session.get(settings.SESSION_ALL_MENU_KEY)
# [{'title': '权限6', 'url': '/return_goods.html', 'menu_id': 17},..]
permission_menu_list = request.session.get(settings.SESSION_PERMISSION_MENU_LIST_KEY)
# 1. 先将所有菜单整理成字典格式,增加三个新的键值对,{1:{'id':1,..., 'children_contents':[], 'status': false, 'open':false},..}
all_menu_dict = {}
for item in all_menu_list:
item['children_contents'] = []
item['status'] = False # 是否显示,false则不显示
item['open'] = False # 是否展开.注意:若子菜单为true, 则所父级菜单必须展开
all_menu_dict[item['id']] = item
# 2. 整理用户权限的菜单列表, 加上三个属性
for item in permission_menu_list:
# 2.1用户有权限的菜单都显示
item['status'] = True
# 2.2匹配出当前访问的url对应的菜单,那么就展开, 例如'/change_goods.html',权限中也有此url,那么就设定open为true
if re.match(item['url'], request.path):
item['open'] = True
else:
item['open'] = False
# 2.3将权限菜单放进主菜单的children_contents列表内
all_menu_dict[item['menu_id']]["children_contents"].append(item)
# 2.4修改菜单的父菜单的status: {1: {'id':1, ..,'children_contents':[title: .., menu_id: 1,..] }}
all_menu_dict[item['menu_id']]['status'] = True
# 2.5修改所有父级菜单的status
pid = all_menu_dict[item['menu_id']]['parent_id'] # pid = 6
while pid:
all_menu_dict[pid]['status'] = True
# (非常关键)因为要不知道有几层菜单标签, 在之前的基础上将父菜单的parent_id作为判断对象,若其不存在,则退出循环
pid = all_menu_dict[pid]['parent_id']
# 方法同上,不断更新pid
# 2.6方法同上,不断更新pid
if item['open']:
pid = item['menu_id'] # pid=14
while pid:
all_menu_dict[pid]['open'] = True
pid = all_menu_dict[pid]['parent_id'] # pid = 6
# 3. 将子菜单装进父菜单的children_contents对应的列表中
for k in all_menu_dict:
pid = all_menu_dict[k]['parent_id']
if pid:
all_menu_dict[pid]['children_contents'].append(all_menu_dict[k])
# 4. 整理出所有的根目录的menu并放入最终列表
ret = []
for k, v in all_menu_dict.items():
if not v['parent_id']:
ret.append(v)
return ret
# 生成html字符串
def produce_html(res_list):
html = ''
# 菜单填充模板, 子菜单放在{1}的位置
tpl1 = """
<div class="rbac-menu-item">
<div class="rbac-menu-header">{0}</div>
<div class="rbac-menu-body {2}">{1}</div>
</div>
"""
# 权限填充模板
tpl2 = '''<a href="{0}" class="{1}">{2}</a>'''
# 循环列表取数据, 逻辑简单的写在前面
for item in res_list:
# 1. 当前菜单的status为False,则不显示
if not item['status']:
continue
if item.get('url'):
# 若当前元素是权限, 则取出数据填充权限列表
html += tpl2.format(item['url'], "rbac-active" if item['open'] else '', item['title'])
else:
# 若当前元素有子菜单, 最难的
if item['children_contents']:
html += tpl1.format(
item['caption'],
produce_html(item['children_contents']),
"" if item['open'] else 'rbac-hide')
return html
// 引入菜单标签到模板中
@register.simple_tag
def rbac_menu(request):
# 1. 判断用户是否登录,即查看有没有权限列表
if not request.session.get('permission_menu_list', False):
return safestring.mark_safe('<h1><a href="/login.html">请先登录</a></h1>')
# 2. 若为登录用户,则调动自定义函数从数据库取到菜单相关的数据
data = process_menu_data(request)
# 3. 生成html, 注意转换成可以渲染的html
html = safestring.mark_safe(produce_html(data))
return html
import os
// 引入css文件到模板中
@register.simple_tag
def rbac_css():
file_path = os.path.join('rbac', 'theme', 'rbac.css')
if os.path.exists(file_path):
return safestring.mark_safe(open(file_path, 'r', encoding='utf-8').read())
else:
raise Exception('rbac主题CSS文件不存在')
// 引入js文件到模板中
@register.simple_tag
def rbac_js():
file_path = os.path.join('rbac', 'theme', 'rbac.js')
if os.path.exists(file_path):
return safestring.mark_safe(open(file_path, 'r', encoding='utf-8').read())
else:
raise Exception('rbac主题javascript文件不存在')
@register.filter
def log_in(value):
if value=='AnonymousUser':
return False
else:
return True
以上是关于Django第10章: 权限管理(递归菜单树)的主要内容,如果未能解决你的问题,请参考以下文章