使用django-guardian实现django-admin的行级权限控制的方法


Posted in Python onOctober 30, 2018

用django框架来做一些后台管理的web页面简直太方便了,django自带模块级的权限系统,用来做一些内部的系统非常合适,可以大大的减少开发量。但是django自带的权限系统还不支持行级的权限控制,如果要实现行级的权限控制,需要依赖第三方的app来开发,或者自己重新写一个。

需求描述

我们项目组开发的一些系统通常会用mysql数据库来存储一些配置,但是如果每次有配置修改的时候都去手动修改mysql数据的话,会挺麻烦的,同时也比较容易出错。django-admin能够根据定义的model自动的生成相应的页面,同时还能提供权限的管理,所以我们就把一些系统到的配置放到django中。但是到现在,随着系统的需求越来越多,该系统已经不止我们自己项目组的人员使用,也要开放给其他项目组的同事使用,所以就产生了一些更细粒度的权限需求。因此,我们要在现有的系统上支持行级的权限控制。

解决方案

当然可以自己写一套权限系统了,但是自己写的成本比较高,而且自己写的不一定比较好。所以我就先在网上找了一些现成的解决方案, https://djangopackages.org/grids/g/perms/ 该链接列出了现有的一些第三方的权限系统解决方案。从该页面来看,django-guardian是最受欢迎的第三方权限系统,而且支持行级的权限系统,同时还可以整合到django-admin里面,所以我就选择了django-guardian。

关键步骤

安装配置django-guardian

安装配置django-guardian比较简单,按照她项目提供的 文档 进行安装就可以了,安装完成后会在数据库里面创建两张权限相关的表。

把django-guardian整合到django-admin

首先把admin.py文件里面需要用到行级权限的类,由原来的继承admin.ModelAdmin,改成继承GuardedModelAdmin,这时候打开某个数据行的页面的时候,在该页面的右上角的历史旁边会显示编辑对象权限的按钮,点击该按钮进去相应的页面就可以编辑该行数据的具体权限。

配置完权限的时候,用一个新的用户测试的话,会发现该用户没有权限来访问任何的数据,这是因为GuardedModelAdmin还有很多事情没有帮我们做,我们还需要重写一些函数来实现admin后台页面的显示。具体的信息看下面的代码注释。

from guardian.admin import GuardedModelAdmin
from guardian.shortcuts import get_objects_for_user, assign_perm, remove_perm, get_users_with_perms, \
  get_groups_with_perms
  
# 需改前
@admin.register(DataAssistantJob)
class DataAssistantJobAdmin(admin.ModelAdmin):
  pass

# 修改后
@admin.register(DataAssistantJob)
class DataAssistantJobAdmin(GuardedModelAdmin):
  # app是否在主页面中显示的话由该函数决定
  def has_module_permission(self, request):
    if super().has_module_permission(request):
      return True
    return self.get_model_objs(request).exists()

  # 在显示数据列表额时候,哪些数据显示,哪些不显示,由该函数控制
  def get_queryset(self, request):
    if request.user.is_superuser:
      return super().get_queryset(request)

    data = self.get_model_objs(request)
    return data
    
  # 内部用来获取某个用户有权限访问的数据行
  def get_model_objs(self, request, action=None, klass=None):
    opts = self.opts
    actions = [action] if action else ['view', 'change', 'delete']
    klass = klass if klass else opts.model
    model_name = klass._meta.model_name
    return get_objects_for_user(user=request.user, perms=[f'{perm}_{model_name}' for perm in actions],
                  klass=klass, any_perm=True)

  # 用来判断某个用户是否有某个数据行的权限
  def has_perm(self, request, obj, action):
    opts = self.opts
    codename = f'{action}_{opts.model_name}'
    if obj:
      return request.user.has_perm(f'{opts.app_label}.{codename}', obj)
    else:
      return self.get_model_objs(request, action).exists()

  # 是否有查看某个数据行的权限
  def has_view_permission(self, request, obj=None):
    return self.has_perm(request, obj, 'view')

  # 是否有修改某个数据行的权限
  def has_change_permission(self, request, obj=None):
    return self.has_perm(request, obj, 'change')

  # 是否有删除某个数据行的权限
  def has_delete_permission(self, request, obj=None):
    return self.has_perm(request, obj, 'delete')

  # 用户应该拥有他新增的数据行的所有权限
  def save_model(self, request, obj, form, change):
    result = super().save_model(request, obj, form, change)
    if not request.user.is_superuser and not change:
      opts = self.opts
      actions = ['view', 'add', 'change', 'delete']
      [assign_perm(f'{opts.app_label}.{action}_{opts.model_name}', request.user, obj) for action in actions]
    return result

通过上面的修改,django-admin中的模块就能够支持行级的权限,并能够正确的在后台页面中显示出来,当然如果有很多的模块需要支持行级的权限控制,则可以把上面的这些修改写到一个新的类中,然后其他想支持行级权限的模块再从该模块继承就可以了。

总结

感觉django-guardian和django-admin整合,实现的不是很好。如果开发者对django内部的代码不怎么了解,那么用django-guardian来实现行级权限控制的话会挺麻烦的,个人认为django-guardian完全可以把和django-admin的整合做到开箱即用的效果,就像django自带的权限系统一样。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
pymssql ntext字段调用问题解决方法
Dec 17 Python
详解C++编程中一元运算符的重载
Jan 19 Python
python+opencv实现动态物体追踪
Jan 09 Python
python3使用smtplib实现发送邮件功能
May 22 Python
Python全排列操作实例分析
Jul 24 Python
python爬取微信公众号文章的方法
Feb 26 Python
python 实现创建文件夹和创建日志文件的方法
Jul 07 Python
Python制作微信好友背景墙教程(附完整代码)
Jul 17 Python
Python中利用LSTM模型进行时间序列预测分析的实现
Jul 26 Python
python bluetooth蓝牙信息获取蓝牙设备类型的方法
Nov 29 Python
Python绘图之二维图与三维图详解
Aug 04 Python
解决pycharm修改代码后第一次运行不生效的问题
Feb 06 Python
解决Shell执行python文件,传参空格引起的问题
Oct 30 #Python
Python格式化输出字符串方法小结【%与format】
Oct 29 #Python
[原创]Python入门教程2. 字符串基本操作【运算、格式化输出、常用函数】
Oct 29 #Python
pycharm执行python时,填写参数的方法
Oct 29 #Python
解决Pycharm下面出现No R interpreter defined的问题
Oct 29 #Python
解决Pycharm运行时找不到文件的问题
Oct 29 #Python
解决Mac下首次安装pycharm无project interpreter的问题
Oct 29 #Python
You might like
php实现设计模式中的单例模式详解
2014/10/11 PHP
关于URL最大长度限制的相关资料查证
2014/12/23 PHP
php精确的统计在线人数的方法
2015/10/21 PHP
laravel实现图片上传预览,及编辑时可更换图片,并实时变化的例子
2019/11/14 PHP
你必须知道的Javascript知识点之"单线程事件驱动"的使用
2013/04/23 Javascript
jQuery中:contains选择器用法实例
2014/12/30 Javascript
jquery实现的美女拼图游戏实例
2015/05/04 Javascript
浅谈JavaScript中的字符编码转换问题
2015/07/07 Javascript
jQuery实现信息提示框(带有圆角框与动画)效果
2015/08/07 Javascript
JavaScript获取服务器时间的方法详解
2016/12/11 Javascript
jQuery插件HighCharts实现的2D对数饼图效果示例【附demo源码下载】
2017/03/09 Javascript
ionic选择多张图片上传的示例代码
2017/10/10 Javascript
使用vue + less 实现简单换肤功能的示例
2018/02/21 Javascript
微信小程序调用微信支付接口的实现方法
2019/04/29 Javascript
[03:39]2015国际邀请赛主赛事首日精彩回顾
2015/08/05 DOTA
Python Socket编程入门教程
2014/07/11 Python
python中Flask框架简单入门实例
2015/03/21 Python
Python下使用Psyco模块优化运行速度
2015/04/05 Python
深入解析Python中的urllib2模块
2015/11/13 Python
django 2.0更新的10条注意事项总结
2018/01/05 Python
python和shell获取文本内容的方法
2018/06/05 Python
Python selenium环境搭建实现过程解析
2020/09/08 Python
Python 生成短8位唯一id实战教程
2021/01/13 Python
CSS3制作Dropdown下拉菜单的方法
2015/07/18 HTML / CSS
html5标记文字_动力节点Java学院整理
2017/07/11 HTML / CSS
英国高档时尚男装购物网站:MR PORTER
2016/08/09 全球购物
德国宠物用品、宠物食品及水族馆网上商店:ZooRoyal
2017/07/09 全球购物
JACK & JONES瑞典官方网站:杰克琼斯欧式风格男装
2017/12/23 全球购物
几个人围成一圈的问题
2013/09/26 面试题
庆六一活动总结
2014/08/29 职场文书
环境工程专业毕业生求职信
2014/09/30 职场文书
初中班主任工作随笔
2015/08/15 职场文书
小学学习委员竞选稿
2015/11/20 职场文书
广播稿:校园广播稿范文
2019/04/17 职场文书
导游词之重庆钓鱼城
2019/09/19 职场文书
MySQL官方导出工具mysqlpump的使用
2021/05/21 MySQL