一级菜单
1.model设计
from django.db import models class Permission(models.Model): url = models.CharField(max_length=64, verbose_name='权限') title = models.CharField(max_length=32, verbose_name='标题') # 备注这个url是做什么的 icon = models.CharField(max_length=64, null=True, blank=True,verbose_name='图标') # 存放菜单前面的图标 is_menu = models.BooleanField(default=False) # 用来判断(此url)是否是菜单 def __str__(self): return self.title class Meta: verbose_name_plural = '权限管理' class Role(models.Model): name = models.CharField(max_length=32, verbose_name='角色') permissions = models.ManyToManyField(Permission, verbose_name='角色拥有的权限', blank=True) def __str__(self): return self.name class Meta: verbose_name_plural = '角色管理' class User(models.Model): username = models.CharField(max_length=32, verbose_name='用户名',unique=True) password = models.CharField(max_length=32, verbose_name='密码') roles = models.ManyToManyField(Role, verbose_name='用户的角色', blank=True) def __str__(self): return self.username class Meta: verbose_name_plural = '用户管理'
注意:数据迁移后,去admin后台界面,设置菜单
2. views.py文件 登录函数中登陆成功后获取当前用户的权限的所有信息(url,title,icon,is_menu)
如:
per = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url', 'permissions__title', 'permissions__icon', 'permissions__is_menu', ).distinct() print(per) ## 打印per的数据 QuerySet[{ 'permissions__url': '/customer/list/', 'permissions__title': '客户列表', 'permissions__icon': 'fa-connectdevelop', 'permissions__is_menu': True}, { 'permissions__url': '/customer/add/', 'permissions__title': '添加客户', 'permissions__icon': None, 'permissions__is_menu': False}, { 'permissions__url': '/customer/edit/(?P<cid>\d+)/', 'permissions__title': '编辑客户', 'permissions__icon': None, 'permissions__is_menu': False}, { 'permissions__url': '/customer/del/(?P<cid>\d+)/', 'permissions__title': '删除客户', 'permissions__icon': None, 'permissions__is_menu': False}, { 'permissions__url': '/payment/list/', 'permissions__title': '账单列表', 'permissions__icon': 'fa-code-fork', 'permissions__is_menu': True}, { 'permissions__url': '/payment/add/', 'permissions__title': '添加账单', 'permissions__icon': None, 'permissions__is_menu': False}, { 'permissions__url': '/payment/edit/(?P<pid>\d+)/', 'permissions__title': '编辑账单', 'permissions__icon': None, 'permissions__is_menu': False}, { 'permissions__url': '/payment/del/(?P<pid>\d+)/', 'permissions__title': '删除账单', 'permissions__icon': None, 'permissions__is_menu': False }]
3、从上述中的数据中挑出菜单列表
# 菜单列表 menu = [] # 权限列表 permission = [] for i in per: permission.append({ 'url': i.get('permissions__url') }) if i.get('permissions__is_menu'): # 如果permissions__is_menu为True就是菜单,放到菜单列表中 menu.append({ 'url': i.get('permissions__url'), 'title': i.get('permissions__title'), 'icon': i.get('permissions__icon') })
4. 为了使两个列表(session中作为值)在session中对应的键的名字可以由用户改变,在名字中设置两个常量
settings.py文件:
# 存放权限的session的key PERMISSION_SESSION_KEY = 'permission' # 存放菜单的session的key MENU_SESSION_KEY = 'menu'
5. 将两个列表放到session中
登录函数,第三步后面
from django.conf import settings request.session[settings.PERMISSION_SESSION_KEY] = permission request.session[settings.MENU_SESSION_KEY] = menu
6. 权限验证的中间件中获取session中的PERMISSION_SESSION_KEY进行权限验证
permission = request.session.get(settings.PERMISSION_SESSION_KEY) # 权限验证(url,中间件中获取的当前url地址如:通过request.path_info获取) for i in permission: if re.match(r'^{}$'.format(i['url']), url): return
7. incluesion_tags(模板中调用函数)
目的:用于前端菜单的展示而编写的函数
函数作用:为菜单添加样式,然后通过装饰器交给特定的模板,
具体如下:
my_tags.py文件
# -*- coding: utf-8 -*- # __author__ = "maple" from django import template from django.conf import settings import re register = template.Library() @register.inclusion_tag('menu_tag.html') # 讲给这个模板进行渲染 def menu(request): url = request.path_info # 获取当前url地址 menu_list = request.session[settings.MENU_SESSION_KEY] # 获取session中的菜单url地址 # 添加激活的样式 for i in menu_list: if re.match(f'^{i["url"]}$',url): # 判断,如果当前页面地址栏中的url地址是菜单中的地址 i['class'] = 'active' # 就给菜单中的地址加上active类选择器。 break return {'menu_list':menu_list} #此时,menu_list列表中的数据为:(前端页面当前停留在客户列表菜单上) [{ 'url': '/customer/list/', 'title': '客户列表', 'icon': 'fa-connectdevelop', 'class': 'active'}, { 'url': '/payment/list/', 'title': '账单列表', 'icon': 'fa-code-fork' }]
8. menu_tag.html 渲染模板
<div class="static-menu"> {% for menu in menu_list %} <a href="{{ menu.url}}" class="{{menu.class}}"> <span class="icon-wrap"><i class="fa {{menu.icon}}"></i></span> {{ menu.title}}</a> {% endfor %} </div>
9.将incluesion_tags应用到前端html页面中
<div class="left-menu"> <div class="menu-body"> {% load my_tags %} # my_tags为存放menu函数的文件名, {% menu request %} # request为第七步中的menu函数的参数 </div> </div>
注意:tags文件都放在templatetags目录中,目录名字不能变,位置随意。
二级菜单
1. models设计
from django.db import models class Menu(models.Model): title = models.CharField(max_length=32, verbose_name='标题') icon = models.CharField(max_length=64, null=True, blank=True,verbose_name='图标') weight = models.IntegerField(default=0, verbose_name='权重') def __str__(self): return self.title class Permission(models.Model): url = models.CharField(max_length=64, verbose_name='权限') title = models.CharField(max_length=32, verbose_name='标题') menu = models.ForeignKey(Menu, null=True, blank=True,verbose_name='菜单') def __str__(self): return self.title class Meta: verbose_name_plural = '权限管理' class Role(models.Model): name = models.CharField(max_length=32, verbose_name='角色') permissions = models.ManyToManyField(Permission, verbose_name='角色拥有的权限', blank=True) def __str__(self): return self.name class Meta: verbose_name_plural = '角色管理' class User(models.Model): username = models.CharField(max_length=32, verbose_name='用户名', unique=True) password = models.CharField(max_length=32, verbose_name='密码') roles = models.ManyToManyField(Role, verbose_name='用户的角色', blank=True) def __str__(self): return self.username class Meta: verbose_name_plural = '用户管理'
2. settings中配置seesion中的键的名字,让key更灵活
# 存放权限的session的key PERMISSION_SESSION_KEY = 'permission' # 存放菜单的session的key MENU_SESSION_KEY = 'menu'
3. 用户登录后,获取登录用户的权限以及菜单信息,并放入session中
def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') obj = models.User.objects.filter(username=username, password=password).first() if not obj: error = '用户名或密码错误' return render(request, 'login.html', locals()) # 获取权限的信息 per = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url', 'permissions__title', 'permissions__menu__title', 'permissions__menu__icon', 'permissions__menu__id', ).distinct() #----------------------------------------------------------------- # 菜单列表(后面转列表) dic = {} # 权限列表 permission = [] for i in list(per): # 获取当前用户的权限列表 print(i, type(i)) permission.append({ 'url': i.get('permissions__url') }) # 获取当前菜单列表 if i['permissions__menu__id']: if dic.get(i['permissions__menu__id']): dic[i['permissions__menu__id']]['children'].append( {'url': i['permissions__url'], 'title': i['permissions__title']}) else: dic[i['permissions__menu__id']] = {'title': i['permissions__menu__title'], 'icon': i['permissions__menu__icon'], 'children': [ {'url': i['permissions__url'], 'title': i['permissions__title']} ] } request.session[settings.PERMISSION_SESSION_KEY] = permission request.session[settings.MENU_SESSION_KEY] = dic #----------------------------------------------------------------- request.session['is_login'] = 1 return redirect('index') return render(request, 'login.html', locals())
4. 中间件中提取权限列表
# -*- coding: utf-8 -*- # __author__ = "maple" from django.utils.deprecation import MiddlewareMixin from django.conf import settings from django.shortcuts import redirect, HttpResponse import re class AuthMiddleware(MiddlewareMixin): def process_request(self, request): url = request.path_info # 白名单 :登陆、注册、admin for i in settings.WHITE_LIST: if re.match(i, url): return # 登陆验证 is_login = request.session.get('is_login') if is_login != 1: return redirect('login') # 免认证 登录之后所有人都能访问的地址 for i in settings.PUBLIC_LIST: if re.match(i, url): return # 权限验证 permission = request.session.get(settings.PERMISSION_SESSION_KEY) for i in permission: if re.match(r'^{}$'.format(i['url']), url): return return HttpResponse('没有权限,请联系管理员!')
5.自定义标签获取菜单信息,通过函数,动态生成二级菜单
# -*- coding: utf-8 -*- # __author__ = "maple" from django import template from django.conf import settings import re register = template.Library() @register.inclusion_tag('menu_tag.html') def menu(request): url = request.path_info menu_list = request.session[settings.MENU_SESSION_KEY] # 添加激活的样式 for k, v in menu_list.items(): print(k,v) """ v ={'title': '客户管理', 'icon': 'fa-connectdevelop', 'children': [{'url': '/customer/list/', 'title': '客户列表'}]} """ for i in v["children"]: if re.match(r'^{}$'.format(i["url"]), url): i["class"] = 'active' break return {'menu_list': menu_list}
<div class="static-menu"> {% for one_menu_id,two_menu in menu_list.items %} <a href="" > <span class="icon-wrap"><i class="fa {{ two_menu.icon }}"></i></span> {{ two_menu.title }}</a> {% for child in two_menu.children %} <a href="{{ child.url }}" class="{{ child.class }}"> <span class="icon-wrap"><i class="fa {{ child.icon }}"></i></span> {{ child.title }}</a> {% endfor %} {% endfor %} </div>
7.应用自定义标签到项目中
<div class="left-menu"> <div class="menu-body"> {% load my_tags %} {% menu request %} </div> </div>
8.使用jQuery实现隐藏二级菜单,点击一级菜单,出现二级菜单
略....................
目录分布