大家好,我是木木。
今天给大家分享一个偏权限规则编排方向的 Python 库,django-rules。
django-rules
它把“谁能做什么”拆成可组合的 predicate,再把这些 predicate 挂到 Django 的权限检查链路里。相比把判断逻辑散落在 view、serializer 和 model 里,这种写法更利于复用、审计和逐步演进。
项目地址:https://github.com/dfunckt/django-rules
三大特点
规则像搭积木一样组合
rules 的核心不是“大而全的权限后台”,而是 predicate。你可以把每个判断拆成最小单元,再用 &、|、~ 组合成真正的业务规则,既直观又容易复查。
既能独立运行,也能接 Django
它底层先是一个通用规则引擎,所以在纯 Python 场景就能跑;一旦项目需要落到 Django 的 has_perm、视图装饰器、Admin 或 DRF,它也能顺着现成接口接进去。
不依赖额外权限表
很多对象级权限方案会把判断结果存到数据库里,而 django-rules 更偏向“运行时判定”。只要用户、对象和上下文信息在手,就可以直接算出结果,维护成本会轻不少。
最佳实践
安装方式:pip install rules
如果你准备接入 Django,别忘了把 rules 放进 INSTALLED_APPS,并把 rules.permissions.ObjectPermissionBackend 加到 AUTHENTICATION_BACKENDS 里。
功能一:先把零散的 if/else 收拢成可复用规则
这段代码解决什么问题:很多团队一开始只是想判断“工单能不能编辑”,结果很快就把 owner、reviewer、状态判断散落到了不同函数里。先用 predicate 把这些条件收拢起来,后面扩展才不会乱。
importrulesfromdataclassesimportdataclass,field@dataclassclassTicket:owner:strreviewers:set[str]=field(default_factory=set)closed:bool=False@rules.predicatedefis_owner(user,ticket):returnticket.owner==user@rules.predicatedefis_reviewer(user,ticket):returnuserinticket.reviewers@rules.predicatedefnot_closed(user,ticket):returnnotticket.closedrules.set_rule("can_edit_ticket",(is_owner|is_reviewer)¬_closed)draft=Ticket(owner="mumu",reviewers={"ops","qa"})archived=Ticket(owner="mumu",reviewers={"ops"},closed=True)foractor,ticketin[("mumu",draft),("ops",draft),("guest",draft),("ops",archived),]:print(f"{actor:>5} -> {rules.test_rule('can_edit_ticket',actor,ticket)}")
这个输出特别适合拿来给产品或后端同事对齐规则边界。谁因为 owner 命中、谁因为 reviewer 命中、谁又因为工单已关闭被挡下来,一眼就能说清。
功能二:把同一套规则直接接进 Django 权限链路
这段代码解决什么问题:业务里真正麻烦的不是“我能不能算出规则”,而是“算出的规则能不能被 Django 原生权限接口复用”。django-rules 的价值就在这里,它让 user.has_perm(..., obj) 能直接走你的 predicate。
importtempfilefrompathlibimportPathDB=Path(tempfile.gettempdir())/"rules_demo2.sqlite3"ifDB.exists():DB.unlink()fromdjango.confimportsettingssettings.configure(SECRET_KEY="demo",USE_TZ=True,DATABASES={"default":{"ENGINE":"django.db.backends.sqlite3","NAME":str(DB)}},INSTALLED_APPS=["django.contrib.auth","django.contrib.contenttypes","rules",],AUTHENTICATION_BACKENDS=("rules.permissions.ObjectPermissionBackend","django.contrib.auth.backends.ModelBackend",),)importdjangodjango.setup()fromdjango.core.managementimportcall_commandcall_command("migrate",run_syncdb=True,verbosity=0)importrulesfromdjango.contrib.auth.modelsimportGroup,UserclassBook:def__init__(self,author,state):self.author=authorself.state=state@rules.predicatedefis_book_author(user,book):returnbool(userandbookandbook.author==user)is_editor=rules.is_group_member("editors")rules.add_perm("library.change_book",is_book_author|is_editor)rules.add_perm("library.publish_book",is_book_author&rules.is_staff)alice=User.objects.create_user("alice")bob=User.objects.create_user("bob")carol=User.objects.create_user("carol")editors=Group.objects.create(name="editors")bob.groups.add(editors)book=Book(author=alice,state="draft")print("alice change :",alice.has_perm("library.change_book",book))print("bob change :",bob.has_perm("library.change_book",book))print("carol change :",carol.has_perm("library.change_book",book))print("alice publish:",alice.has_perm("library.publish_book",book))

这就是它最实用的地方。同一套规则不只在“业务函数”里能测,还能直接喂给 Django 的认证后端。作者本人可以改书,编辑组成员也可以改,但发布权限因为还叠了 is_staff 条件,所以作者本人并不会自动获得。
环境与版本信息
高级功能
django-rules 不只会做“布尔判断”,它还有一套 invocation context,可以让同一轮规则求值过程共享上下文数据。这个能力很适合把一次昂贵计算缓存下来,后面的 predicate 直接复用。
这段代码解决什么问题:当你的权限判断依赖上下文信息时,不一定要在每个 predicate 里重复计算。先缓存,再复用,组合表达式会更清晰。
importrules@rules.predicate(bind=True)defcache_region(self,user,service):region=service["region"]self.context["region"]=regionprint("cache region :",region)returnTrue@rules.predicate(bind=True)defsame_region(self,user,service):print("reuse cached data :",self.context.get("region"))returnuser["region"]==self.context.get("region")can_deploy=cache_region&same_regionprint("cn user ->",can_deploy.test({"region":"cn"},{"region":"cn"}))print("us user ->",can_deploy.test({"region":"us"},{"region":"cn"}))
这个例子虽然简单,但非常能体现 rules 的味道。前一个 predicate 把 region 放进上下文,后一个 predicate 直接复用,不需要把相同数据再查一遍。规则一复杂,这种写法会比大段嵌套判断好维护得多。
适用场景
- Django 项目已经有认证体系,但对象级权限逻辑开始变复杂
- 想把权限判断从 view、serializer、service 里抽出来统一管理
- 需要一套既能独立测试、又能挂到
has_perm 上的规则表达方式 - 团队希望后续再接 Admin、CBV、DRF 时还能延续同一套规则
不适用场景
- 需求只是极简单的角色判断,原生 Django 权限已经足够
- 规则判断高度依赖数据库里的审批流状态机,且希望直接查表得结果
- 当前项目更需要的是完整 RBAC 平台,而不是轻量规则引擎
上线检查
- 先把 predicate 命名成业务语言,再组合成 rule,避免后面只剩一坨布尔表达式。
- 接入 Django 时确认
AUTHENTICATION_BACKENDS 顺序和 INSTALLED_APPS 配置正确,再做一次真实的 has_perm 回归。 - 对复杂规则补充单元测试,尤其是 owner、group、上下文条件叠加时的边界分支。
总结
django-rules 的厉害之处,不是把权限系统做重,而是把“规则”这件事做轻、做清楚、做得能复用。