Django默认Group和Permission不够用,因其仅支持模型级静态权限(add/change/delete/view)和组分配,无法满足真实业务中字段级过滤、对象属性动态判断(如作者且草稿状态)、多条件组合(如财务组+华东区+金额阈值)等细粒度需求。

为什么 Django 默认的 Group 和 Permission 不够用?
Django 自带的权限系统基于模型级(add_/change_/delete_/view_)和组(Group)分配,但真实业务中常需:按字段过滤数据(如“只能看自己部门的订单”)、按对象属性动态判断(如“编辑文章仅限作者且状态为草稿”)、或组合多条件(如“财务组+华东区+金额user.has_perm() 或 @permission_required 直接覆盖。
常见错误现象包括:
- 硬编码部门 ID 或角色名在视图里,导致权限逻辑散落、难以审计
- 用
filter(user=request.user)替代权限检查,漏掉跨模型关联约束(如“编辑订单需同时是客户经理且该客户已签约”) - 把所有逻辑塞进
get_queryset(),结果 API 和管理后台行为不一致
用 django-rules 实现对象级规则定义
django-rules 是最轻量且契合 Django 原生风格的方案:它不替换默认权限系统,而是作为补充,在需要时注入上下文感知的判断逻辑。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 安装后,在
rules.py中定义规则函数,参数必须是user和obj(可为None表示模型级) - 规则函数返回
True/False,**不能抛异常**;Django 会自动缓存结果,避免重复计算 - 注册规则时用
@rules.predicate装饰器,再调用rules.add_perm("app.change_order", is_order_owner | is_manager) - 在视图中统一用
rules.has_perm("app.change_order", request.user, order),而非混用user.has_perm()和手动 if 判断
示例规则:
提供使用 django-unfold (v0.56+) 构建高级 Django 管理界面的专业指导。当用户提及 django-unfold、unfold admin 等内容时使用此技能。
@rules.predicate
def is_order_owner(user, obj):
return obj and obj.created_by == user
@rules.predicate
def is_department_manager(user, obj):
return obj and user.department == obj.department
rules.add_perm("sales.change_order", is_order_owner | is_department_manager)
如何让管理后台(admin)也遵守细粒度规则?
Django Admin 默认只认 user.has_perm(),不会自动调用 rules.has_perm()。必须显式重写四个关键方法:
-
has_view_permission(self, request, obj=None):控制列表页和详情页可见性 -
has_change_permission(self, request, obj=None):控制编辑按钮、表单字段可写性 -
has_delete_permission(self, request, obj=None):控制删除操作 -
get_queryset(self, request):**最关键**——必须在这里过滤 queryset,否则用户可通过 URL 直接访问未授权对象
注意:get_queryset 返回的结果必须与权限判断逻辑一致。例如规则中用了 is_department_manager,这里就要 .filter(department=request.user.department),否则会出现“列表看不到但 URL 能打开”的越权漏洞。
什么时候该用 django-guardian 而不是 rules?
当权限需要“为单个对象单独赋权”,且该赋权关系要持久化到数据库(比如给某篇文章指定 3 个协作者,每个协作者有不同字段编辑权限),django-guardian 更合适。它的核心是 assign_perm("blog.change_article", user, article),权限绑定到具体对象实例。
但要注意:
- 每条对象级权限都生成一条 DB 记录,高并发场景下可能成为性能瓶颈
- 不支持复杂逻辑(如“用户 A 对文章 B 的编辑权限仅在发布时间后 24 小时内有效”),这类仍需结合
rules在运行时判断 - 与 Admin 集成需额外配置
GuardedModelAdmin,且get_queryset()仍需手动调用get_objects_for_user()
多数场景下,rules + 前端/URL 层过滤 + Admin 的 get_queryset 重写,已经能覆盖 90% 的 RBAC 需求。真正需要 guardian 的,往往是协作类 SaaS 应用中的“分享链接”“协作者邀请”等明确对象授权场景。
最容易被忽略的是:规则函数里对 obj 的判空处理——传入 None 时代表模型级权限(如列表页),此时不能直接访问 obj.field,否则引发 AttributeError;还有就是 get_queryset 中漏掉对 obj=None 场景的兼容,导致列表页权限失效。

















