1.权限控制的流程+表结构html
内容回顾:前端
wsgi:socket进行收发消息python
中间件:(超级重点的面试题)在全局范围内控制django的输入和输出的一个钩子,处理输入和输出说白了就是处理请求和响应request对象和response对象,他说的是一个全局的钩子,认为是全部的请求都要进来,钩子的概念是什么?只要把功能写上去就能运行,中间件注册上就能用,注销了,整个东西就没有了,可插拔性很是好,写好了就能用,没写好就不能用,提早预留好了.说到这里,咱们知道form里边有局部钩子和全局钩子,web
session是将数据存储到内存中,数据的读取会很是的快.链接数据库的话也是能够的,每次登录都须要进行读取一遍,速度回慢一些,压力比较大.面试
下面,咱们分析一下上边的流程,看一下都完成了哪些事情,第一次,假如说是没有登陆的话,直接访问某个页面,好比说是customer_list,浏览器进来,走wsgi.封装成request对象,来走中间件,中间件里边定义了一些权限控制的中间件,中间件里边有(白名单),白名单没有经过校验,进入登陆状态的校验,没有登陆状态,这个时候就会返回页面,也就是redirect重定向,本质上就是一个响应头,返回给了浏览器,浏览器看到以后,再次向login发送一个登陆的get请求,请求进来先走wsgi,再走过了中间件的白名单,经过白名单,向后走,走到urls.py匹配,访问login的视图函数,执行view里边的视图函数,而后打印模板,渲染一个login的登陆页面,这样往回走,走到中间件的时候,须要再走全部中间件的process_response方法,虽然咱们写的中间函数中没有,可是其余中间件中可能有,再走wsgi,再回到浏览器.浏览器就能够看到登陆页面,登陆页面中输入用户名和密码,发送POST请求,再走wsgi,而后走中间件,依然是login白名单,再走urls.py,而后走views.py的视图函数,在views.py进行校验用户名和密码,这个时候,走model,而后从数据库中db进行查询,查询以后,返回查询的结果,数据库返回一行行数据,有了models以后,才封装成models对象,这个时候在view.py拿到models对象,所以,咱们本身建立的models对象,和在数据库中经过ORM拿到的models对象是同样的,区别是,本身建立的话,本身填充数据,ORM操做是从数据库中拿到数据,而后封装成对象,本身封装的对象.save会保存到数据库中,或者拿到一个对象修改它的属性,再去save,会到数据库中进行修改,没用查找对象,认证失败,返回给浏览器须要从新登陆,再次发POST请求.假如认证成功了,权限信息的初始化,保存的信息有哪些,保存权限的list,保存登陆状态,保存菜单的dict,存入session中,返回重定向,这个时候,给的是index走到浏览器返回的是location,sql
地址写的是index,浏览器再次发送一个请求index,走wsgi,再走中间件,获取到地址是index,白名单校验一下,发现不是白名单,接着往下走,可是数据库
是一个登陆状态,接着日后走,发现是一个免认证的地址,而后return,再走到urls.py,再次走到view视图中,返回一个index页面,这个时候用到了母版和继承,继承了layout.html,layout里边有咱们自定义的menu标签,{% menu request%},这个地方用到了inclusiontag,结果是动态的html字码段,经过代码块,在大模板里边套了一个小母版,而后放在母版里边了,最后经过index进行继承,再把index写的字符串,添加到block块里边,最后咱们拿到,大模板套着小母版,这个html页面,而后返回给浏览器,这个时候就用到了,动态生成菜单的结果,用到的是inclusiontag,这个时候浏览器显示的是有权限的信息了,点开的是一级菜单,二级菜单是咱们能够访问的一些权限信息,而后点击二级菜单,先看图
django
点击进入一级菜单,显示二级菜单,显示咱们能够访问的一些权限,咱们再点击二级菜单,开始发送请求,再走wsgi,再走中间件,一次经过(白名单,登陆状态的校验,免认证的地址)都不是,下面咱们开始进行权限的校验,须要,获取当前用户的权限信息,这个时候,从session中拿到权限信息,而后和当前的地址,作一一的匹配,能匹配成功说明有这个权限,return就往回走,若是都没有匹配成功,就没有权限,就直接返回了给浏览器,这个是权限的控制,正常状况下有菜单是有权限的,咱们经过菜单点出来,都是有权限的,日后走,好比"视图",再经过model,再拿数据库中的数据,返回一个queryset一个对象列表,而后咱们再套模板,而后再渲染用户的一个个数据,用到了模板的继承,这个时候layout和{%menu request%}再次使用了,生成一个完整的html页面,再返回给浏览器,这个时候能够看到用户展现的列表,剩下的页面都是一样的效果,json
代码有哪几部分?浏览器
中间件和rbac相关的,因此把中间件写在rbac里边,
登陆用户和密码,这是登陆相关的业务逻辑,须要写在业务的app里边,
权限信息的初始化和业务逻辑是没有太多关系的,只不过是登陆成功以后所作的事情,咱们也把这部分代码拿出来,放在rbac的app里边,
只要能找到,调用使用就能够了,这个时候咱们是须要动态生成菜单的也就是 母版和继承&&layout&&{%menu request%}
这个html也是和权限rbac相关的,根据权限生成的菜单,把这部分也写在rbac的template里边的tags,也叫rbac进行区别,也就是和控制相关的,
还有一个关键的东西,settings.py,进行相关的配置.
访问127.0.0.1:8000至关因而访问127.0.0.1:8000/,(也就是指的是访问根目录)这个地址是没有访问权限的,
咱们访问的是127.0.0.1:8000/login/地址,经过白名单以后拿到的页面.
输入root用户&&密码123
下面咱们梳理下这个流程:
首先咱们看中间件,也就是settings.py,
django帮助咱们封装的中间件,请求进来经过这个中间件以后,才会封装成request.session,出去的时候,在视图中用到了request.session赋值,也就是在视图中出去时,再次通过request_session,会对用户进行设置,用到这个时候,咱们须要注销的,
csrf是用来作校验的,
最下面的rbac是咱们本身注册的中间件,咱们须要看下这个流程.
下面是settings.py里边的一些设置
咱们向login发送post请求用户名和密码,
第一个白名单url匹配成功就return,再路由匹配,也就是项目的根的路由匹配,admin不是,访问这个空的
点击进入include里边的urls,咱们将就进入二级url里边看一下,
找到路径以后,咱们进入"视图",
若是用户名或密码错了,下面的obj就是None,结果就会返回登陆页面,咱们须要从新登陆login,显示"用户名或密码错误"
经过密码和用户了,咱们就须要惊醒"权限信息初始化(权限,菜单)",点击进入init_permission函数
obj表明用户对象,obj.roles表明管理对象,帮助咱们管理多对多的关系对象,
后边调用filter或者all才能拿到queryset,将角色的权限为空的权限去除掉
思考,多表查询的方式,有哪几种?
子查询,查询完以后再去另外一张表中查询,
连表查询:内链接,左链接,右链接,
内链接只会罗列出:两张表中都有的信息,
左链接是左边有右边没有,右边就补空,
目前,咱们的状况是,就是补空的形式,角色表有这个形式,对应的权限没有id,和角色id对应匹配上,因此数据局势空,也就是这个filter里边为空的设置,要去除掉空的数据(url权限为空的部分),而后咱们经过values取对应字段的值.最后的结果就是queryset,里边的形式就是一个字典,
找权限表里边的url和title,以及经过全下表再找到菜单表里边的title&&icon&&id.
上图中,再往下走是,构建权限的列表permission_list,菜单字典menu_dict
最终咱们把结果存放到session当中了,
咱们将session存放的键,放在settings.py中
感受绕的话,也能够写死,可是咱们能够经过配置,优化了程序,和上边同样
下面,咱们看一下构建的数据结构:
permission_query指的就是每个字典的权限信息,
下边一个构建的是权限列表,一个是构建的菜单字典
上边就是构建一级和二级菜单的地址
初始化,完成以后,咱们再重定向到index首页
这个时候咱们再访问中间件rbac.py
url=index...,白名单也不是,获取登陆状态,也已经在permission.py里边设置过了,而后,
再看一下,是否是免认证的状态,若是是里面认证的,在settings.py里边有免认证的地址,
而后return,日后走,路由的匹配,先项目,在二级路由,找到视图函数index.py
在index页面里边,咱们先继承,而后写钩子里边的东西:
用上图所示的内容,至关于调用相关的函数,menu,执行menu,点击menu进去查看一下,在
menu_dict就是获取菜单的数据结构:
获得下图这样的字典:
上图包含一级和二级菜单的信息,
而后咱们返回菜单字典里的值
也就是找到装饰器里边的menu.html进行循环出来
生成菜单列表以后,咱们就将生成的结果放在母版页layout.html里边的,下图对应的位置:
上图中的menu指的是函数名.这样就包含模板里边生成的内容
同时看到的首页,也就会将index填充到layout.html中对应的block对应的前端位置.以下图
而后交还给浏览器,就能够看到对应的页面
二级菜单的难点:1构建数据结构,2inclusion_tag优势难度(记住这个inclusion_filter||inclusion_simple)记住用法和显示的结果就能够了,三个方法其实都是模板中调用函数3.表结构和对应的含义4.对应的流程图
发出一个请求对应一个响应,与下次请求没有关系,若是想要有关系,须要保存一个session 5.以及模板里边的对应关系等等.
今日内容:
(1)一级菜单排序
(2)二级菜单选中而且展开
(3)非菜单权限的归属
(4)路径导航
(5)权限粒度控制到按钮级别
2.一级菜单排序
标准:什么样的权限在上边,什么样的权限在下边,这个须要咱们先规定好,如何处理?须要有依据,加东西,一级菜单排序
原来的菜单表样子:
下面,咱们须要对它加上对应的权重,进行排序,
二级菜单结构对应的样子,以下图:
修改以后的样子:
只须要修改数据库对应的权重大小,就能够修改对应的顺序.
咱们是在menu.html中循环生成的,循环下图字典中1和2对应的值
目前的问题:字典是无序的
python3.5字典是无序的
python3.6字典显示是有序的,内部是无序的
python3.7才真正的是和插入顺序是一致的.
为了保证字典有序,咱们须要用到有序字典模块.
collections包中的orderedDict类 form表单中的setfields,也就是定义字段中的顺序,后边是字段对象.
字典的排序:
按照插入顺序排序,如何排序呢?sorted()
sorted默认是升序排列,加上reverse=True就是降序排列
上图咱们循环的是menu_list里边的键,将顺序按照键来进行排序
咱们如今的需求是按照wight来进行排序
,如何处理?结合匿名函数处理下面的一些关系,按照从大到小排序
修改参数,再看一下
如何实现这个功能呢?思考一下子
首先执行数据库迁移
咱们须要将右侧的数据库移除,从新拖动进去sqllites,而后打开menu表,修改一下权重,将1改为100
建议中间空上几个值,方便插入其余的菜单
下面咱们处理顺序,注意这个时候咱们是在自定义的里边进行处理,
原来的样子:
在permission中再加上一个筛选的权重wight,
这个时候,咱们按照降序排列,数值大的在前面
而后赋值,循环就能够了,另外一个就是,咱们如今返回的值是有序字典的值
这个时候,运行,从新登陆一下root,咱们看一下结果:(运行报错,将上边的ret=的里边的weight改为wight)
这个时候,财务管理就在上边了,
咱们现再修改一下数据库的权重,刷新页面看一下结果
结果没有发生改变,缘由是session中的数据,不会由于你修改数据库就修改session里边的数据.
只有再次登陆才会修改结果,下图是从新登陆以后的结果:
二级菜单若是想排序也是能够加上权重,构造的时候,须要将下图的数据排好才行
3.二级菜单默认选中并展开
首先,咱们拿当前的url地址,而后循环每个标签的地址,匹配上咱们就加active,这样就结束了,如今的状况是一级菜单套二级菜单,如今咱们想拿二级菜单里边的url地址
原来的写法:
item表明一级菜单的结果,咱们须要循环二级菜单的结果,看下面的写法:
item里边的i表明二级菜单里边的一个个字典,而后正则匹配.
如今咱们拿到的是children里边的字典,见下图:拿下面的url
下面咱们开始匹配当前的地址,拿到以后加上active
循环完成以后,咱们再交给menu.html页面进行渲染
匹配完成以后,咱们再处理,
运行程序:
解释:如今咱们作的效果是,在原来的基础上在浏览器点击到二级菜单,浏览器的搜索框添加上对应的搜索地址
如今咱们获得的结果默认,都是展开的状态
须要在body上加上hide
也就是在menu菜单里边加上hide
这个时候,全部的都是闭合的,
所以,hide咱们不能写死,想要去掉,须要用js去掉
咱们能够默认是展开的,而后进行操做
下图中item表明一级菜单的信息,咱们能够设置
如今咱们在一级菜单都加上hide
下面的两个菜单都是hide,
如今也是表示默认都是关闭的
如今咱们应该访问哪一个页面,哪一个页面就应该是展开的状态,也就是访问二级菜单,应该显示的是选中的状态.
也就是,如今咱们进去了,hide改为空字符串""
这个时候,我么再运行程序,选中的页面展开,
咱们须要的效果是,点击就打开,点击另外一个,就关上上一个打开的菜单.
咱们须要经过js实现代码
原来的js代码:
运行程序:
js也在其余地方可能用到,也是须要拿出来的
这个时候,咱们只须要在模板页中引入便可,咱们就成功修改了二级菜单的问题
4.非菜单权限的归属
咱们点击上图中的"添加客户",获得下图:
这个时候,咱们不但愿菜单栏关上,
左侧菜单:
客户管理
展现客户 (一对多的关系)
添加客户
编辑客户
删除客户
...
财务管理
缴费列表
...
权限表:
id url title menu_id parent_id
1 /customer/list/ 展现客户 5(菜单id的1) null
2 /customer/add/ 添加客户 null 1(id为1)
3 /customer/edit/(d+) 编辑客户 null 1
咱们如今须要作的是产生关系在 展现客户和添加客户之间
而且是1对多的关系.
咱们让"展现客户"成为"父选项",添加,编辑,删除,成为子选项
思考,如何实现上边的表结构?
咱们须要作路由匹配:
咱们须要作的事情是,不论是访问什么只要找到: 添加和编辑删除的url对应的父亲就能够了,
咱们的目的是:找到父亲url就能够了,
咱们访问二级菜单,找到对应的url就能够了
点击客户,找到对应的父权限.不论是访问二级菜单仍是子权限,咱们只须要找到对应的父亲就能够了
本身关联本身1对多
下面运行,数据库迁移
如今,咱们在admin里边添加权限信息
下面,咱们开始获取了
在这里只是拿本身的关系字段:
下面咱们在"菜单字典"里边添加内容
咱们打印一下二级菜单里边的内容
服务端信息
这个时候,咱们就拿到了二级菜单的权限信息,
在菜单里边,咱们须要进行配置,菜单里边已经有了,咱们须要加上本身的id
咱们再次访问这个地址:
获得的结果:
也就是url的地址.
再走展现客户的地址
咱们再从新登陆一下:
如今,咱们获得的是二级菜单里边的内容:
咱们如今,访问"展现客户"里边的内容:
服务端获得的结果:
倒数第三行的id是二级菜单的id,
咱们再看一下url地址的匹配:
服务端对应所示的内容:
咱们在这里的i拿到的是中间件的权限信息,
须要获取这个东西
咱们获取的是两种请求的方式字典
第一种访问的是"父权限",第二种是"子权限"
咱们须要的是父权限的id,应该如何处理这个问题?
这个时候,咱们保存的就是二级菜单的id
咱们再将存储的id和打印的id对比一下.
i在这里表示的是二级菜单的字典,
咱们就将这里的二级菜单的id和咱们刚才存储的id作比较.
比对成功,咱们就将下图的地址作相关的展现
原来的代码:
修改以后的代码:进行比较
思路,咱们不论是获取的是二级菜单仍是二级菜单里边的内容,都要把二级菜单获取到
这个时候的小问题:(访问index的时候,咱们找不到)
走中间件,从上到下,从"白名单"到"获取登陆状态",再到免认证,最后走到下图所示的内容:
所以,咱们须要在一开始定义一个默认值,
这个时候,咱们再刷新:
咱们再设置一个settings.py里边的一个配置
思考一下,这个时候,走到中间件应该怎样用?
中间件配置完成再用反射进行设置
这个时候,咱们再在自定义的rbac里边取值
这样咱们就将程写活了,
咱们只是换了一种写法,
反射的方法很方便,可是反射的流程会复杂一些,也能够先写死,多看几遍再写.
71-5最后2分钟复原,若是须要的话
回复以前的样子:也能够反着回复过去
5.
下午回顾:
咱们思考一下能不能优化一下程序:
原来的样子:
优化后的样子:
上边方块是循环前,下边是循环后的代码块
两连等:
复习的内容:
1. 一级菜单的排序 有序字典 sorted(menu_dict, key=lambda x: menu_dict[x]['wight'], reverse=True) 2. 二级菜单选中而且展开 hide active 3. 非菜单权限的归属 客户管理 展现客户 添加客户 编辑客户 删除客户 财务管理 缴费列表 权限表 id url title menu_id parent_id 1 /customer/list/ 展现客户 5 null 2 /customer/add/ 添加客户 null 1 3 /customer/edit/(\d+)/ 编辑客户 null 1
5.路径导航,也就是下图中的花红框的内容:
咱们须要添加url地址和标题
咱们须要将写死的变成动态的导航效果:
这个时候layout.html就再也不写死了,将下图红框内容注释掉
i是定义的字典,下面咱们经过循环出来.
当走到上图的权限校验,咱们就知道访问的是哪一个地址了
服务端里边没有title,因此咱们须要加上,见下图
下图是咱们筛选出的title信息
上图中的权限列表加上title,
这个时候,咱们从新登陆一下root
这个时候,已经能够成功显示了
这个时候,咱们看到已经存在了title
这个时候,咱们再点击"添加缴费记录"
这个时候就没有了
由于这个时候走的是if里边,见下图
下面咱们进行尝试,把面刀导航栏也加到子权限中,看结果
见上图,如今就有了"添加缴费"
见上图,"编辑缴费"也有了
如今惟一少的是父亲的导航标签
经过打印,咱们能够看到部分添加到列表的信息
打印的位置是中间件,基于角色的权限访问控制
如何经过服务端的信息拿二级菜单的title.
如何更方便的取到?
思考,可不能够将列表换成字典?
将上图的id当作key
看一下组织数据结构的思路:
从上边的结构修改为下边的结果,会更好处理一些
下面咱们将权限的列表修改为权限的字典
原来列表的添加方式:
如今的添加的方式:
下图这个session中存储的地方要修改为权限的字典
必须改为这样,不然下边会报错
存储的时候会产生影响,天然取的时候也会产生影响,
中间件rbac须要修改的内容:
修改后的结果:
这个时候二者都用到,咱们就将语句放在最下边
运行:
缘由是咱们没有登陆:
如今咱们从新登陆root&&123试一下
报错:
以上是一些错误缘由,须要进行修改:
这个时候,再运行,
登陆:
点击"添加客户"
也就是咱们拿pid的时候出错了
拿到的是1,为何报错?
细节问题
咱们将data1这样的一个字典,存储到session中,作序列化,原来的状况是数字,咱们如今作了json的序列化以后,字典的键是数字的话,会转化成"字符串",
见下图:
这个时候,原来的状况变成了字符串了,咱们对中间件进行操做:
这个时候,咱们再次点击"添加客户"
成功获得下图:
咱们将导航栏写在,母版页面
咱们须要再定义一个inclusion_tag,和上边的菜单栏是同样的
应该如何操做呢?
如今咱们只须要拿breadlist就能够了
由于上边已经写了,load,咱们只须要写自定义函数的请求就能够了
这个时候,咱们刷新页面:
如今咱们存在的问题是小问题是,在当前页面的时候不须要,点击"缴费列表",到最后一个不用点就好了
咱们只须要修改循环的次数,进行判断:
这样最后一个就没有a标签了,经过id判断
经过子权限找到父权限的信息,
咱们将权限列表修改为了权限字典,json字符串的转化,最后一个面包屑的位置没有a标签
6.权限粒度控制到按钮级别
咱们须要作的是有权限就展现,没有权限就不展现了,这个就是权限粒度控制到按钮级别
咱们如今须要作的是对应用户可以展现的结果:
咱们将全部的权限放到一个列表当中,if和else进行判断
拿什么表明权限?url
什么和url有关系?
路由系统里边的name也能表明一个url
咱们将用户所拥有的的name收集起来,
咱们还能够经过customer_list.html进行相关的反向解析,url和name是1对1对应的.
若是在列表中,咱们就进行展现,没有在列表中咱们就不进行展现
name 如今咱们思考应该怎么处理?
咱们须要在rbac文件夹下的models.py里边的Permission类(也就是表)中,再添加一个属性.可是这个名字必须惟一,若是表中已经建立了,不要先加惟一,须要进行一些处理
咱们先演示一下上边操做以后的错误显示,
1)须要在提供一个一次性默认值,
2)须要再添加一个default
咱们选择1
随便写一个"xx"
报错:
报错
由于咱们向数据库中存储数据有约束,因此,所以咱们不能给url默认值,不能是同样的
咱们先删掉,数据库迁移里边的name
咱们再将上边的name的惟一值去掉,
再次执行命令,以及提供默认值
再执行上图,咱们将成功生成到数据库当中去了,
这个时候,当前的全部字段都是xxx
这个时候,咱们在url中,先加上name以后,咱们再加上约束
咱们知道web当中的urls.py
咱们将别名一次添加到权限列表中:
点击上传:
处理完成以后,咱们再添加约束
若是表是空的,咱们一开始就能添加,可是咱们的表不是空的,须要先加上默认值,再加上修改以后的不一样值,也就是别名以后,
咱们须要再添加unique=True
这个时候,咱们再执行命令:
这个时候,就没有问题了.
这样咱们就把数据保存起来了,咱们如今须要找的是列表或者集合进行处理
咱们能够在permission中再次添加一个数据结构,添加以后保存在session中,可是没有必要,咱们还能够怎样操做呢?
咱们如今拿到的是权限的id作的是key,见下图,
id的特色是惟一,不重复.name加上unique至关因而惟一的,不重复的,咱们也能够吧name当作key进行处理.
咱们须要将权限id,修改为权限name
原来的代码
如今的代码:
之后咱们能够经过keys拿到全部的权限了
修改以后,存在一个问题,路径导航会找不到了,缘由是什么?
缘由是数据结构发生了改变:这个时候从"子权限"找"父权限"已经找不到了
咱们能够换一种方式,找parent_name
下图是修改以后的数据结构:
查询下面的权限表中的父亲,而后找名字name,是两个下划线
注意,后边的"逗号"必定要加上
这个时候就查询拿到父权限的name,
下面,咱们在打印一下构建的字典:
登陆root&&123
运行:http://127.0.0.1:8000/login/
登陆root&&123
服务器端,这个时候能够获得字典:
这个时候,咱们获得的data2和data3是同样的
如今咱们再修改一下中间件内部的信息
修改以后的内容:
展现客户没有问题:
添加客户也没有问题:
注释,下边两行的内容:
如今,咱们直接判断字典:
咱们看一下settings.py中的信息
上边,咱们作的是,若是有这个权限就显示,没有这个权限,咱们就不显示
运行,登陆qiangge&&123帐户
登陆以后,只有一个"展现客户"的权限
咱们将权限的别名当作字典的key
上边的if end写法写死了,下面咱们开始,用filter进行再次定义一下
咱们再将a i a注释掉
刷新:
登陆root帐户,依然获得的是下面的结果:
咱们如今,仍是只须要,作简单的if判断就好了
咱们再定义一个,
若是两个都没有权限,如何处理?
编辑和删除,存在一个咱们就显示,不存在就再也不显示了
选项里边,也须要加上这部份内容:
这个时候,咱们再次登陆qiangge&&123,看一下显示
这个时候,由于没有权限,就不在显示"编辑"和删除了
咱们给"秘书",再添加一个"编辑用户",点击保存.
再次登陆qiangge&&123
这个时候,就能够登录了
粒度显示,就是作if...else判断,有就显示,没有就不显示,表结构上的改变,name也就是别名,记录url,而且不能重复,有了这个条件,咱们就能够作字典中的key了
修改上图的时候,会对"子权限"找"父权限",产生影响,这个时候,咱们用pname找,也能够找到了,
咱们自定义的这个filter方法,就须要判断当前的name是否是在权限字典里边,若是存在,直接return,不存在,则None,
须要生成页面的话,尤为是须要控制到按钮级别,就须要将判断一个一个加到按钮连接上边,咱们须要对每个按钮作判断,咱们作的事情就是麻烦的事情.
,还须要大力复习,前面的知识点.