Django中带模型层级关联约束的行级权限实现方案咨询
嘿,我太懂你这个困扰了!Django自带的权限系统在处理这种模型层级关联下的行级权限继承时确实有点捉襟见肘——默认的权限要么针对整个模型,要么是单个实例的独立权限,没法自动跟着外键关系递推你想要的层级约束,比如你说的“有某城市的权限就能查看其下所有图书馆,但不能看图书”,或者“有图书馆权限就能看其下所有图书”这种场景,默认系统根本搞不定。
给你几个圈内常用的成熟方案,不用自己从零搭规则引擎:
django-guardian:这是行级权限的老牌工具,核心能力就是给单个模型实例分配权限。要实现你的层级约束,你可以自己扩展它的逻辑:比如封装一个通用的权限检查方法,当判断用户是否能访问某个
Library时,先看他有没有这个Library的直接权限,没有的话再往上查所属City的权限;检查Book时,按Book→Library→City的顺序依次验证。你可以把这个逻辑写成模型的实例方法,比如has_hierarchical_perm(user, perm),或者自定义一个视图装饰器,在视图里自动做这个层级检查。django-rules:这个简直就是为你一开始想的“规则型授权”量身定做的!它允许你用函数/装饰器定义复杂的权限规则,完美支持基于模型关联的层级判断。比如你可以这样定义规则:
from rules import predicate, rule @predicate def can_access_library(user, library): # 优先检查直接权限 if user.has_perm('view_library', library): return True # 继承所属城市的权限 return user.has_perm('view_city', library.city) @predicate def can_access_book(user, book): # 检查直接权限 if user.has_perm('view_book', book): return True # 继承所属图书馆的权限 if can_access_library(user, book.library): return True # 还可以直接跳级检查城市权限 return user.has_perm('view_city', book.library.city) # 把规则和权限绑定 rule('can_access_library')(can_access_library) rule('can_access_book')(can_access_book)之后在视图或者模板里直接用
rules.test_rule('can_access_book', user, book)就能快速判断,而且它还支持把规则持久化到数据库——如果之后需要动态调整层级权限规则,不用改代码直接在后台配置就行,非常灵活。django-role-permissions:如果你更偏向于基于角色的权限体系,这个包可以帮你快速定义角色,然后给角色绑定带层级关联的权限。比如给
CityManager角色分配view_city权限,同时扩展角色的权限检查逻辑,让这个角色自动拥有该城市下所有Library的view_library权限,基础框架已经很完善,只需要少量定制就能贴合你的需求。
要是你不想引入第三方包,自己写轻量版也不难:
- 给每个模型添加自定义的权限检查实例方法,比如
Book模型里的user_can_view(user),内部依次检查自身、关联的Library、关联的City的权限; - 或者自定义Django的
ModelBackend权限后端,重写has_perm方法,当检查实例权限时自动向上遍历外键关联,验证上级实例的权限。
不过说实话,我最推荐django-rules,它完全匹配你一开始设想的“智能、可持久化的规则型授权”思路,不用你自己去处理规则的存储和逻辑复用,省超多事!
备注:内容来源于stack exchange,提问作者laith drebaty

