一、动态显示二级菜单
上篇随笔中,我们实现了动态显示一级菜单,现在考虑这样一种情况,用户的菜单权限比较多,这个时候全部并列展现在左侧菜单就不合适了,所以,现在有这样一个需求,即把用户的菜单权限分类,划分成二级菜单,动态显示在左侧菜单,解决方案如下:
1、修改权限表结构
(1)分析需求,要求左侧菜单如下显示:
客户管理:
客户列表
账单管理:
账单列表
(2)修改rbac下的models.py,修改后代码如下:
from django.db import models class User(models.Model): """ 用户表 """ name = models.CharField(verbose_name='用户名', max_length=32) password = models.CharField(verbose_name='密码', max_length=32) roles = models.ManyToManyField(verbose_name='拥有的所有角色', to='Role') def __str__(self): return self.name class Role(models.Model): """ 角色表 """ title = models.CharField(verbose_name='角色名称', max_length=32) permissions = models.ManyToManyField(verbose_name='拥有的所有权限', to='Permission') def __str__(self): return self.title class Permission(models.Model): # 建立与菜单类的关联,去掉is_menu和icon """ 权限表 """ url = models.CharField(verbose_name='含正则的URL', max_length=32) title = models.CharField(verbose_name='标题', max_length=32) menu = models.ForeignKey(verbose_name='所属菜单', to="Menu", on_delete=models.CASCADE, null=True) def __str__(self): return self.title class Menu(models.Model): # 新增一个菜单表 title = models.CharField(max_length=32, verbose_name='菜单') icon = models.CharField(max_length=32, verbose_name='图标', null=True, blank=True)
2、注入session(重点是构建数据结构),setsession.py中代码修改如下:
def initial_session(user_obj, request): """ 将当前登录人的所有权限url列表和自己构建的所有菜单权限字典注入session :param user_obj: 当前登录用户对象 :param request: 请求对象HttpRequest """ # 查询当前登录人的所有权限列表 ret = Role.objects.filter(user=user_obj).values('permissions__url', 'permissions__title', 'permissions__menu__title', 'permissions__menu__icon', 'permissions__menu__id').distinct() permission_list = [] permission_menu_dict = {} for item in ret: # 获取用户权限列表用于中间件中权限校验 permission_list.append(item['permissions__url']) menu_pk = item['permissions__menu__id'] if menu_pk: if menu_pk not in permission_menu_dict: permission_menu_dict[menu_pk] = { "menu_title": item["permissions__menu__title"], "menu_icon": item["permissions__menu__icon"], "children": [ { "title": item["permissions__title"], "url": item["permissions__url"], } ], } else: permission_menu_dict[menu_pk]["children"].append({ "title": item["permissions__title"], "url": item["permissions__url"], }) print('权限列表', permission_list) print('菜单权限', permission_menu_dict) # 将当前登录人的权限列表注入session中 request.session['permission_list'] = permission_list # 将当前登录人的菜单权限字典注入session中 request.session['permission_menu_dict'] = permission_menu_dict
3、从session中取出菜单权限信息,修改my_tags.py文件,代码如下:
from django.template import Library register =Library() @register.inclusion_tag("menu.html") def get_menu_styles(request): permission_menu_dict = request.session.get("permission_menu_dict") return {"permission_menu_dict": permission_menu_dict}
4、渲染页面,修改menu.html文件,代码如下:
<div class="multi-menu"> {% for item in permission_menu_dict.values %} <div class="item"> <div class="title"> <i class="{{ item.menu_icon }}"></i>{{ item.menu_title }} </div> <div class="body"> {% for foo in item.children %} <a href="{{ foo.url }}">{{ foo.title }}</a> {% endfor %} </div> </div> {% endfor %} </div>
到此,二级菜单已经渲染到页面中了,样式或者其他js效果就靠你的前端技术了!
二、补充知识点
1、DATABASES的使用
我们知道django项目使用数据库要在settings.py中通过设置DATABASES给项目配置数据库引擎,我们之前都是给项目设置成MySQL或者django默认的sqlite3数据库,大家知道一个项目可以有多个应用,并且事实也是如此的,每个项目下也会自己的模型类,按照之前的做法,在定义了数据库引擎之后做数据库迁移,那么每个app下的数据库都使用settings中指定的数据库了,但是你可能会有这样的需求,就是不同的应用使用不同的数据库,该如何做呢?没错,也是设置DATABASES,方式如下:
DATABASES = { 'default': { # 下面没有指定的都使用default下的数据库引擎 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, 'app01':{ # app01使用mysql数据库引擎 'ENGINE': 'django.db.backends.mysql', 'NAME': 'bms', # 要连接的数据库,连接前需要创建好 'USER': 'root', # 连接数据库的用户名 'PASSWORD': '', # 连接数据库的密码 'HOST': '127.0.0.1', # 连接主机,默认本机 'PORT': 3306 # 端口 默认3306 } }
2、ForeignKey的参数db_constraint解释
在学习数据库时,我们通过定义外键使两个表建立连接和约束,但有时我们需要只建立连接,不建立约束,也就是通过在表中建立另一个表的id,不设置foreignkey约束。
Django的ForeignKey也为我们想好了这两种情况:
(1)db_constraint = True 表示两个表之间既建立了连接又有约束,并支持ORM语法查询;
(2)db_constraint = False 表示两个表之间只建立了连接,没有建立约束,并支持ORM语法查询;
注意:当然你可能会想到能否将ForeignKey改为一个IntegerField,通过定义IntegerField字段的值来与另一个表建立联系,这种方法也没错,但是这样就不能使用ORM语法查询,也就享受不到ORM查询的方便,为开发造成了困难,所以不推荐这样做!