bigywater.tech
Django · 77

Groups、Permissions 与权限检查

用户有没有某个权限,Django 怎么算?为什么刚给用户加了权限,has_perm 还是 False?对象级权限支持吗?这篇逐条验证。

缓存
has_perm 的结果缓存在 user 对象上,同一对象上授予权限后不会立刻生效
is_active
超级用户如果 is_active=False,has_perm 也是 False
对象级
默认后端的 has_perm(perm, obj) 恒为 False(超级用户除外)

默认权限

实测 · 本机执行输出
['add_article', 'change_article', 'delete_article', 'publish_article', 'view_article']

权限行由 post_migrate 信号创建,所以新加的模型要迁移之后才有权限行。

has_perm 的规则

实测 · 本机执行输出
没有任何权限: False
直接授予 add_article 之后(还是同一个 user 对象): False  ← 权限缓存!
重新从数据库取用户: True
加入组后(新取的用户,还没有查过权限): True
超级用户 has_perm(任意字符串): True
超级用户但 is_active=False: False
has_perm 需要 "app_label.codename" 格式,只写 codename: False

需要 app_label.codename 全名。组的权限和直接授予的权限取并集。

权限缓存

实测 · 本机执行输出
第一次 has_perm:查询 2
    SELECT "django_content_type"."app_label" AS "content_type__app_label", "auth_permission"."codename" 
    SELECT "django_content_type"."app_label" AS "content_type__app_label", "auth_permission"."codename" 
之后再查其它权限:查询 0  (_perm_cache / _user_perm_cache / _group_perm_cache 在对象上)
缓存属性: ['_group_perm_cache', '_perm_cache', '_user_perm_cache']

在 request.user 上调用 has_perm 时,缓存存在这个对象上,随请求结束消失,所以「每个请求最多查两次库」。但在测试或脚本里长期持有同一个 user 对象时容易踩坑。

对象级权限

实测 · 本机执行输出
has_perm("lab.change_article") = True  has_perm("lab.change_article", obj) = False
超级用户带 obj: True

Django 提供了 obj 参数的接口,但默认的 ModelBackend 不实现。

分步演示:权限检查

权限
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。SQLite
    • 没有验证:Permission 与 ContentType 缓存(ContentType.objects 的缓存)、权限检查在 admin 里的使用、组权限的变更传播、自定义后端实现对象级权限
    京ICP备2026067502号-1京公网安备11010502063487号