动态菜单 一级菜单的实现:
1.先解释一下权限:
首先我们要知道,我们是根据权限,来决定当前这个用户可以做什么样的操作。那么问题来了, 我要如何在 页面上展示, 不同的用户访问时, 他有哪些可以操作的按钮。
比如: CEO来的时候,他可以做任何事情。那么我就需要把所有的 按钮给他展示出来。
可以添加,可以查看,可以编辑,可以修改。 那么最少就需要4个按钮。
销售专员,登录的时候。 他只有 查看,和添加的功能。 那么我就只需要给他展示两个按钮。
2.然后再说一说菜单:
菜单是一个可以点击之后,我能够看到,这个菜单下 拥有哪些功能。 或者哪些 二级菜单。
菜单是一个 有标题 有 url 的这个一个按钮。 那么我们的权限表 也是这样的结构。 但是还有一个问题,我们的权限表中有哪些记录(也就是哪些url 可以当作菜单使用)。
客户列表 毋庸置疑,这个可以用来当作一个菜单。 添加客户也可以当作一个菜单(当然也可以将他当作 客户列表菜单下的一个功能)。 编辑和删除,就不能作为一个菜单。 因为编辑和删除都是 要对一个指定的客户进行操作了。 如果作为菜单使用,我们就需要在菜单下面,把所有的客户全部都列出来。 这么做太傻了。
我们要的应该是这样一个效果 说再多也不如一张图实在 上图:
这是一张整体的效果图, 信息管理是一级菜单。他没有URL。只是用来区分不同功能的二级菜单。客户列表和账单列表就是二级菜单了。二级菜单就有URL了,我这里都是查看页面的URL作为二级菜单:
3. OK 效果大概是这样, 那么我们要怎么去实现:
3.1 修改我们的权限表, 添加两个字段。 这个字段用来表示 我这条记录(url) 是否可以被当作,权限使用。(我们把这个功能搞成由使用者来决定), 另一个就是图标了。
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题', max_length=32)
url = models.CharField(verbose_name='含正则的URL', max_length=128)
is_menu = models.BooleanField(verbose_name="是否可作为菜单", default=False)
# 默认为False 因为菜单少, 功能多。
icon = models.CharField(verbose_name="图标", max_length=32, null=True, blank=True)
def __str__(self):
return self.title
不要好奇,为什么图标是CharField。 因为我想使用 http://fontawesome.dashgame.com/ 这个网站的图标库。 只需要为标签添加一个 类。 就能拥有丰富的图标。 简单实用为啥不用。
而且 只是需要下载一下,他的css框架, 并放到我们的static中, 最后再模板里面引入一下就ok。
<link rel="stylesheet" href="{% static 'plugins/font-awesome/css/font-awesome.css' %} "/>
3.2 登录的步骤,添加一个session记录。 这个记录保存 当前这个用户拥有的权限当中,可以被当作菜单来使用的 这条url
因为我是开发rbac组件,所以添加session。 当然还是要着这个组件里完成喽。毕竟 web业务的 login视图,只是调用一下:上代码
1 from django.conf import settings
2
3
4 def init_permission(current_user, request):
5 '''
6 :param current_user: 当前请求 用户对象
7 :param request: 当前请求 数据
8 :return:
9 '''
10 # 2. 权限 初始化
11 # 根据当前用户信息,获取当前用户所拥有的所有的权限(queryset对象 是不能直接放入,session中的)
12 permission_queryset = current_user.roles.filter(permissions__isnull=False) \
13 .values("permissions__url", "permissions__is_menu", "permissions__title", "permissions__icon") \
14 .distinct()
15
16 # 获取权限 和 菜单信息。 权限放在权限列表,菜单放在菜单列表
17 menu_list = []
18 permission_list = []
19 for item in permission_queryset:
20 permission_list.append(item.get("permissions__url"))
21 if item.get("permissions__is_menu"):
22 temp = {
23 "title": item.get("permissions__title"), # 标题
24 "icon": item.get("permissions__icon"), # 图标
25 "permissions__url": item.get("permissions__url"), # 对应的跳转url
26 }
27 menu_list.append(temp)
28 request.session[settings.PERMISSIONS_SESSION_KEY] = permission_list
29 request.session[settings.MENU_SESSION_KEY] = menu_list
rbac\service\init_permission.py
这里因为 又多了一个要添加到session中的数据。 但是循环遍历的还是原来的数据。所以做一点小优化。一次遍历,取出所有的url 和 可用作菜单的url。
我这里用字典是因为,要进行模板渲染的时候,比较方便。
3.3 然后就是,从session中取出,权限列表,进行验证。菜单列表,进行渲染菜单的工作了。
这里使用一个 inclusion_tag() 的,自定义模板语法的装饰器。 他的作用是 将被装饰函数的 返回值。传进模板中进行渲染,然后返回给调用者渲染完成的html字符串。
自定义模板语法的代码:
from django.template import Library
from django.conf import settings
register = Library()
@register.inclusion_tag("rbac/static_menu.html")
def static_menu(request):
'''
创建一级菜单
:return:
'''
path_info = request.path_info
menu_list = request.session.get(settings.MENU_SESSION_KEY)
return {"menu_list": menu_list, "path_info": path_info}
<div class="static-menu">
{% for menu in menu_list %}
{% if path_info == menu.permissions__url %}
<a href="{{ menu.permissions__url }}" class="active">
<span class="icon-wrap"><i class="fa {{ menu.icon }}"></i></span>{{ menu.title }}</a>
{% else %}
<a href="{{ menu.permissions__url }}">
<span class="icon-wrap"><i class="fa {{ menu.icon }}"></i></span>{{ menu.title }}</a>
{% endif %}
{% endfor %}
</div>
OK 模板语法已经搞定了。 如何使用呢:
直接到我的业务app中,调用这个 模板就可以。传入相应的参数。
{% load rbac_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{% static 'plugins/font-awesome/css/font-awesome.css' %} "/>
<title>Title</title>
</head>
<body>
<div class="left-menu">
<div class="menu-body">
{% static_menu request%}
</div>
</div>
</body>
</html>
<--我这里只是为了 简洁,所以只是展示一下,使用的方法-->
<-- 在我调用{% static_menu request%} 的地方。 传入request参数。最终返回的就是已经 渲染好了的 html字符串-->