动态菜单 一级菜单的实现:
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视图,只是调用一下:上代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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
这里因为 又多了一个要添加到session中的数据。 但是循环遍历的还是原来的数据。所以做一点小优化。一次遍历,取出所有的url 和 可用作菜单的url。
我这里用字典是因为,要进行模板渲染的时候,比较方便。
3.3 然后就是,从session中取出,权限列表,进行验证。菜单列表,进行渲染菜单的工作了。
这里使用一个 inclusion_tag() 的,自定义模板语法的装饰器。 他的作用是 将被装饰函数的 返回值。传进模板中进行渲染,然后返回给调用者渲染完成的html字符串。
自定义模板语法的代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<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中,调用这个 模板就可以。传入相应的参数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{% 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字符串-->