crm-4权限
1.rbac-优化login函数
因为login是业务逻辑 ,而rbac是个组件 ,将rbac在login的代码分离
###初始化权限函数分离出去 rbac/service/permission from untitled import settings def init_permission(request, obj): permission_query = obj.roles.all().filter(permissions__url__isnull=False).values( 'permissions__title', 'permissions__url', 'permissions__is_menu', 'permissions__name', ).distinct() permission_dict = {} menu_list = [] for i in permission_query: permission_dict[i['permissions__name']] = {'url': i['permissions__url']} if i['permissions__is_menu']: menu_list.append({'title': i['permissions__title'], 'url': i['permissions__url']}) # 将对象列表转换为列表 ! 并将用户权限存入session request.session[settings.PERMISSION_SESSION_KEY] = permission_dict request.session[settings.MENU_SESSION_KEY] = menu_list # 此刻该用户登录成功 request.session['is_login'] = True ###login函数直接使用 class login(View): def get(self, request): return render(request, 'login.html') def post(self, request): name = request.POST.get('username') password = request.POST.get('password') md5 = hashlib.md5() md5.update(password.encode('utf-8')) md5_pwd = md5.hexdigest() print(md5_pwd, name) obj = models.User.objects.filter(name=name, password=md5_pwd).first() if not obj: return HttpResponse('账号密码错误') # 初始化用户权限 else: init_permission(request, obj) return redirect(reverse('crm:deplist'))
2.业务-保留搜索条件
问题: 在第9页修改了某条数据提交后 ,直接跳到了第1页 ,这是因为视图函数返回到了展示页面没有携带页码
解决方法: 1.点击修改后的页面url要有上个页面的页码(前端) 2.点击提交数据跳转的页面携带当前页面的页码(view函数)
思想: 关注request,确保每次访问的url都有页码参数 ,因为request每次请求都会变化,所以每次都要加参数
定义simple_tag函数返回增加了page参数的url ,前端a标签调用函数获得url
request.GET.urlencode() 是当前url上的参数
###/rbac/middlewares/my_define @register.simple_tag def reverse_url(request, *args, **kwargs): # get的参数 params = request.GET.urlencode() # 跳转的url args[0]是url别名 args[1]是url别名参数 url = reverse(args[0], args=(args[1],)) return '{}?{}'.format(url, params) ###前端html中 执行函数 {% if request|has_permission:'dep_edit' %} <td> <a href={% reverse_url request 'crm:depedit' obj.pk %}><i class="fa fa-pencil-square-o" aria-hidden="true">   </i></a> <a class="b1" del_id="{{ obj.pk }}" style="color: red"><i class=" fa fa-remove" aria-hidden="true"></i></a></td> {% endif %}
修改视图函数中的redirect的值 ,直接拼接上url的参数
url_name定义为原来reverse的无参数的url
##通用工具utils/reverse_url from django.urls import reverse def reverse_url(request, url_name, *args, **kwargs): url = reverse(url_name) params = request.GET.urlencode() return '{}?{}'.format(url, params) ##跳转页面修改视图函数dep.py def depadd(request): .... return redirect(reverse('crm:deplist')) ...
3.权限二级菜单
一级菜单为信息管理
二级菜单为班级列表与部门列表
一级菜单为用户管理
二级菜单为用户列表
思路: 如果要完成二级菜单 ,我们需要以一级菜单为主导 ,子信息包含二级菜单所有信息 ,根据这个想法我们将取出的权限信息进行分析
1) 完成models的设计
新增Menu表用来存储一级菜单 ,和一级菜单的其他信息(样式什么的)
class Menu(models.Model): title = models.CharField('菜单名称', max_length=32) icon = models.CharField('样式', max_length=128) def __str__(self): return self.title
修改Permission表中的is_menu的字段 ,将该字段直接删除 ,信增外键关联Menu表 ,此时所有关联Menu表的权限都是二级菜单
class Permisssion(models.Model): url = models.CharField('含正则url', max_length=128) title = models.CharField('中文权限标题', max_length=32, blank=True, null=True) name = models.CharField('url的别名', max_length=32, unique=True) # is_menu = models.BooleanField('是否为菜单', default=False) menu = models.ForeignKey('Menu', blank=True, null=True) def __str__(self): return self.title
修改admin中原来的is_menu消失的字段 ,做数据迁移
2)新增一级菜单数据 --> 并将权限关联一级菜单
名称:信息管理 样式:fa-eercast
名称:用户管理 样式:fa-address-book
3)重新从权限中获取的管家数据permission_query
通过角色表的权限外键获取 : 权限的名字 权限的标题 权限的url 权限的外键id是否为空
通过角色表的权限外键获取到权限表的Menu外键 : 一级菜单的标题 一级菜单的样式
##原来数据格式
permission_query = obj.roles.all().filter(permissions__url__isnull=False).values(
'permissions__name',
'permissions__title',
'permissions__url',
'permissions__menu_id',
'permissions__menu__title',
'permissions__menu__icon',
).distinct() # 对列表中重复的字典进行去重
##生成菜单字典的1示例格式 :一级菜单收缩下拉框, 二级菜单作为子值负责跳转url menu_id:{ menu_title: 信息管理, menu_icon: fa-eercast, children: [ { permissions__title: '班级列表' ,url:'/crm/class/list' }, { permissions__title: '部门管理' ,url:'/crm/dep/list' } ]
}
from untitled import settings def init_permission(request, obj): # permission_query是该用户的所有权限 ,从角色表出发 ,根据外键的权限表打印出所有的所需要信息 permission_query = obj.roles.all().filter(permissions__url__isnull=False).values( 'permissions__name', 'permissions__title', 'permissions__url', 'permissions__menu_id', 'permissions__menu__title', 'permissions__menu__icon', ).distinct() # 对列表中重复的字典进行去重 permission_dict = {} menu_dict = {} for i in permission_query: # 生成权限字典 permission_dict[i['permissions__name']] = {'url': i['permissions__url']} # 生成一级菜单字典 if i['permissions__menu_id'] not in menu_dict: menu_dict['permissions__menu_id'] = { 'title': i['permissions__menu__title'], 'icon': i['pemissions__menu__icon'], 'children': [ {'title': i['permissions__url'], 'url': i['permissions__url']} ] } # 如果字典中有一级菜单的key就可以直接追加一个children了 else: menu_dict['permissions__menu_id']['children'].append( {'title': i['permissions__url'], 'url': i['permissions__url']}) # 将对象列表转换为列表 ! 并将用户权限存入session request.session[settings.PERMISSION_SESSION_KEY] = permission_dict request.session[settings.MENU_SESSION_KEY] = menu_dict # 此刻该用户登录成功 request.session['is_login'] = True
4) inclusion_tag修改支持一级菜单二级菜单显示 ,并新增权重排序字典 ,和非菜单归属(需要新增weight字段与parent字段 )
@register.inclusion_tag('menu.html')
def menu(request):
"""拿出当前url
给所有的一级菜单都hide隐藏
再循环二级菜单 ,如果当前地址匹配到了二级菜单 ,给二级标签重点标记active ,并把一级菜单取出hide
修改后的菜单字典传给前端
"""
# 这个url是当前访问的url
url = request.path_info
menu_dict = request.session[settings.MENU_SESSION_KEY]
permissions_dict = request.session[settings.PERMISSION_SESSION_KEY]
# 非菜单权限归属重新定义url解决
for i in permissions_dict.values():
if re.match('^{}$'.format(i['url']), url) and i['parent']:
# 这里将url直接替换为费权限归属的parent__url ,因为该url就是用来生成菜单的
url = i['parent']
break
# 根据权重将menu_dict变为有序字典
sort_dict = OrderedDict()
temp_lst = sorted(menu_dict, key=lambda x: menu_dict[x]['weight'], reverse=True)
for i in temp_lst:
sort_dict[i] = menu_dict[i]
for i in sort_dict.values():
# 开始获取将全部的隐藏 ,仅将匹配到的url取消隐藏
i['class'] = 'hide'
for child in i['children']:
if re.match('^{}$'.format(child['url']), url):
child['class'] = 'active'
i['class'] = ''
return {'menu_dict': sort_dict.values()}
4.面包屑功能
1)拿到对应的数据放入权限字典
permission_dict[i['permissions__name']] = {'url': i['permissions__url'], 'title': i['permissions__title'], 'parent': i['permissions__parent__url'], 'parent_title': i['permissions__parent__title']}
2)inclusion_tags中定义数据结构 ,将一级菜单和非权限菜单分离 ,放入breadcrum_list列表中
@register.inclusion_tag('breadcrumb.html') def breadcrum(request): url = request.path_info permission_dict = request.session[settings.PERMISSION_SESSION_KEY] # 导航数据 breadcrum_list = [{'title': '首页', 'url': '/crm/dep/list/', }] for i in permission_dict.values(): # 二级菜单 if re.match('^{}$'.format(i['url']), url) and not i['parent']: breadcrum_list.append({'title': i['title'], 'url': i['url'], }) break # 非权限菜单 if re.match('^{}$'.format(i['url']), url) and i['parent']: breadcrum_list.append({'title': i['parent_title'], 'url': i['parent'], }) breadcrum_list.append({'title': i['title'], 'url': i['url'], }) break return {'breadcrum_list': breadcrum_list}
3)html中展示 ,如果是最后一个不用a标签 ,layout模板引用
####breadcrumb.html
<ol class="breadcrumb"> {% for breadcrum in breadcrum_list %} {% if forloop.last %} <li>{{ breadcrum.title }}</li> {% else %} <li><a href="{{ breadcrum.url }}">{{ breadcrum.title }}</a></li> {% endif %} {% endfor %} </ol>
###layout.html
<div class="right-body">
<div>
{% breadcrum request %}
</div>
....
5.使用redis存储session
1)搭建redis
docker pull redis
docker run -p 6379:6379 -v /redis:/data -d redis --appendonly yes
docker exec -it 容器ID redis-cli -h 192.168.1.30 -p 6379
2)django配置
pip install django-redis-sessions==0.5.6
settings.py配置
SESSION_ENGINE = 'redis_sessions.session' SESSION_REDIS_HOST = '192.168.1.30' SESSION_REDIS_PORT = 6379 SESSION_REDIS_DB = 1 #指定存到redis的一个库中,默配置16个库 SESSION_REDIS_PASSWORD = '' SESSION_REDIS_PREFIX = 'session'
3)验证
docker exec -it 容器ID redis-cli -h 192.168.1.30 -p 6379
select 1 #切库
keys * #看到session
flushdb #清空后web端退出登录