说起权限我们大家都知道,不一样的角色会有不一样的权限。比如就像学生管理系统一样,管理员,老师,学生之间的权限都是不一样的,那么展示的页面也是不一样的。所以,我们现在来看看具体操作。
目标:生成一个独立的组件,到哪都能用
一、先创建一个项目,建一个app01和rbac的应用
1、先看配置文件合适不,给创建的rbac在配置文件里面设置一下找到INSTALLED_APPS=['rbac']
配置静态文件
二、表结构设计
2、设计表结构models中创建类:五个类,七张表角色表:用户表:权限表:
权限组表:
菜单表:
角色表和权限表是多对多的关系(一个角色可以有多个权限,一个权限可以对应多个角色)用户表和角色表是多对多的关系(一个用户可以有多个角色,一个角色有多个用户)
所以有会多生成两张关系表
一个菜单下面有多个组
一个组下面有多个菜单
一个菜单下面有多个权限
1、我们一般是先看到的是列表页面,在这个页面上是否显示添加,是否显示编辑,是否显示删除,都是需要判断的有无添加权限,有无删除权限,有无编辑权限,我们可以给每一个url一个代号
dict={1:{代号/userinfo/list/userinfo/add/add/userinfo/del(\d+)/del/userinfo/edit(\d+)/edit}}不仅在列表页面需要知道他有那些权限,在其他页面也知道他有那些权限所以上面的方案还是有点不好,那么我们采取下面的方案。将代号取出来放在一个列表里面
dict={1:{"codes":["list","add","del","edit"]urls:["/userinfo/","/userinfo/add"/,"/userinfo/del(\d+)/","/userinfo/edit(\d+)/",]}2:{"codes":{"list","add","del","edit"}urls:["/order","/order/add"/,"/order/del(\d+)/","/order/edit(\d+)/",]}}把这个字典存到session中当你访问页面的时候我就知道你有什么权限一个url对应一个code多个url对应一个组
注意:关联字段null=True数据库用的时候可以为空关联字段blank=Trueadmin用的时候可以为空当出现这个错误的时候
解决办法
pythonmanage.pymigrate--fake废弃三、通过django-admin录入权限数据
-先创建一个超级用户python3manage.pycreatesuperuser-用户名root-密码zhy123456-在admin.py中fromrbacimportmodelsadmin.site.register(models.Permission)admin.site.register(models.Role)admin.site.register(models.UserInfo)这样的话上去的是英文的,如果你想让中文显示就在类中加一个类classMeta:verbose_name_plural="权限表"-当你给关联字段录入数据的时候会有错误提示,那么在类中你的那个关联字段在加一个属性blank=True可以为空permissions=models.ManyToManyField(to="Permission",verbose_name="具有的所有权限",blank=True)
结构化数据:方便以后做操作。。。
dict={1:{"codes":["list","add","del","edit"]urls:["/userinfo/","/userinfo/add"/,"/userinfo/del(\d+)/","/userinfo/edit(\d+)/",]}2:{"codes":{"list","add","del","edit"}urls:["/order","/order/add"/,"/order/del(\d+)/","/order/edit(\d+)/",]}}5.拿到用户请求的url去session里面做验证获取当前请求的url获取session中保存当前用户的权限然后开始验证如果匹配成功就有权访问如果匹配不成功就无权访问用re去匹配的时候,re.match(/userinfo/,/userinfo/add)#都能匹配到那么要记得在匹配正则的时候加个起始符和终止符regex="^{0}$".format(url)deflogin(request):.....设置sessiondefindex(request):....获取sessiondefuserinfo(request):获取session这样如果有好多个函数,就的重复好多代码,我们可以用中间件来处理中间件和装饰器的区别:中间件用来做批量处理如果函数不多的话可以用加装饰器的方法
五、中间件:获取session,并且当用户匹配成功的时候,先把code保存在request中,方便以后判断
1、记得要配置白名单
2、必须继承MiddlewareMixin这个类
classMiddlewareMixin(object):def__init__(self,get_response=None):self.get_response=get_responsesuper(MiddlewareMixin,self).__init__()def__call__(self,request):response=Noneifhasattr(self,'process_request'):response=self.process_request(request)ifnotresponse:response=self.get_response(request)ifhasattr(self,'process_response'):response=self.process_response(request,response)returnresponse六、设计权限管理-----问题:在访问列表页面时,是否需要判断有无添加权限、有无删除权限、有无编辑权限。
views
defuserinfo(request):#方式一#Page_permission=request.permission_code_list#方式二:实例化page_permission=BasePagePermission(request.permission_code_list)print("page_permission",request.permission_code_list)data_list=[{"id":1,"name":"xxx1"},{"id":2,"name":"xxx2"},{"id":3,"name":"xxx3"},{"id":4,"name":"xxx4"},{"id":5,"name":"xxx5"},]returnrender(request,"userinfo.html",{"data_list":data_list,"page_permission":page_permission}在模板userinfo.html中:两种使用方式
方式一:
方式二:
2、怎么让这些菜单分级显示并且如果当前访问的url权限默认展开如果是组内菜单就加粗或者变红
3、非菜单url,默认选中原菜单。(如果你是点击用户列表进来的,那么你看到页面了,如果你点击添加的时候,你的那个用户列看不见了,这就不好了。所以要设计当你点击添加按钮的时候,那个用户列表被默认选中)
菜单管理菜单一用户管理权限管理菜单二订单管理角色管理
在表里面设计了一个组内菜单(自关联),当menu_gp_id为NULL就代表可以作为菜单
1、在初始化的时候,初始化权限信息,获取权限信息并放置到session中
menu_list=[]foriteminpermission_list:tpl={"id":item["permissions__id"],"title":item["permissions__title"],"url":item["permissions__url"],"menu_gp_id":item["permissions__menu_gp_id"],"menu_id":item["permissions__group__menu_id"],"menu_title":item["permissions__group__menu__caption"]}menu_list.append(tpl)request.session[settings.PERMISSION_MENU_KEY]=menu_list
因为是要在页面上渲染,一般我们会在视图函数的render里面加{"":变量}这样渲染,但是还有个更好用的方法:用自定义的标签
具体操作:1、找到app创建一个templatetags的文件夹2、然后在里面随便创建一个文件3、导入formdjango.templateimportLibraryregister=Library()
方式一:@register.simple_tagdefmenu():return"菜单"这里返回啥页面上就显示啥然后在母版里面导入mnue.html{%loadrbac%}
方式二:@register.includsion_tag("xxx.html")#这里存放的是html文件,,,@register.includsion_tag("xxx.html")自动会读这个文件并且把返回值拿到在页面上渲染defmenu():return"菜单"这里返回啥页面上就显示啥“在母版中:{%menu_htmlrequest%}request是参数,记得要加上{%loadrbac%}
4、注意:如果有两个文件夹同名,避免发生冲突:就再创建一个文件夹包起来
menu_list=[{'id':1,'title':'用户列表','url':'/userinfo/','menu_gp_id':None,'menu_id':2,'menu_title':'菜单二'},{'id':2,'title':'添加用户','url':'/userinfo/add/','menu_gp_id':1,'menu_id':2,'menu_title':'菜单二'},{'id':3,'title':'删除用户','url':'/userinfo/del/(\\d+)/','menu_gp_id':1,'menu_id':2,'menu_title':'菜单二'},{'id':4,'title':'编辑用户','url':'/userinfo/edit/(\\d+)/','menu_gp_id':1,'menu_id':2,'menu_title':'菜单二'},{'id':5,'title':'订单列表','url':'/order/','menu_gp_id':None,'menu_id':1,'menu_title':'菜单一'},{'id':6,'title':'添加订单','url':'/order/add/','menu_gp_id':2,'menu_id':1,'menu_title':'菜单一'},{'id':7,'title':'删除订单','url':'/order/del/(\\d+)/','menu_gp_id':2,'menu_id':1,'menu_title':'菜单一'},{'id':8,'title':'编辑订单','url':'/order/edit/(\\d+)/','menu_gp_id':2,'menu_id':1,'menu_title':'菜单一'}]②然后循环列表找出可以作为菜单的权限
{1:{'id':1,'title':'用户列表','url':'/userinfo/','menu_gp_id':None,'menu_id':2,'menu_title':'菜单二'},5:{'id':5,'title':'订单列表','url':'/order/','menu_gp_id':None,'menu_id':1,'menu_title':'菜单一'}}③再次循环列表向上边的字典中添加active
{1:{'id':1,'title':'用户列表','url':'/userinfo/','menu_gp_id':None,'menu_id':2,'menu_title':'菜单二','active':True},5:{'id':5,'title':'订单列表','url':'/order/','menu_gp_id':None,'menu_id':1,'menu_title':'菜单一'}}④结构化数据(吧上面得到的数据化成下面这样格式的,方便以后使用)
{1:{'menu_id':1,'menu_title':'菜单一','active':None,'children':[{'title':'订单列表','url':'/order/','active':None}]}2:{'menu_id':2,'menu_title':'菜单二','active':True,'children':[{'title':'用户列表','url':'/userinfo/','active':True}]},}