django自带user和group仅支持全局功能级权限,无法实现“编辑某篇文章”等对象级控制;需用django-guardian等方案扩展,其通过独立表存储用户/组对具体对象的权限。

为什么 Django 自带的 User 和 Group 不够用
因为真实业务里权限往往不是“用户属于某个组就全开”,而是细粒度到“能否编辑某篇文章”“能否查看某客户订单”。Django 默认的 is_staff、is_superuser 和基于 Group 的 permission 只能解决“全局功能开关”,没法处理对象级(object-level)控制。比如:销售 A 能看自己提交的客户资料,但不能看销售 B 的——这得靠自定义逻辑或第三方库。
用 django.contrib.auth.decorators.permission_required 控制视图入口
适合功能级权限,比如“只有有 blog.add_post 权限的人才能访问新建文章页”。它底层检查的是 User.has_perm('app_label.action_model'),所以必须先在模型中定义 Meta.permissions:
class Post(models.Model):
title = models.CharField(max_length=100)
class Meta:
permissions = [
("publish_post", "Can publish post"),
]
然后在视图中使用:
from django.contrib.auth.decorators import permission_required
@permission_required('blog.publish_post', raise_exception=True)
def publish_view(request):
...
- 注意
raise_exception=True,否则无权限时会跳转到登录页,容易误判为“没登录” - 权限字符串必须和
Meta.permissions中定义的 codename 严格一致,不能写成blog.can_publish_post - 该装饰器只校验请求方法为 GET/POST 的入口,不校验表单提交后端逻辑是否重做权限检查
在视图函数里手动调用 user.has_perm() 或 user.has_module_perms()
比装饰器更灵活,适合需要动态判断的场景,比如根据 URL 参数决定是否放行:
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
if not request.user.has_perm('blog.view_post', post):
raise PermissionDenied
return render(request, 'post_detail.html', {'post': post})
-
has_perm()第二个参数传模型实例,才能触发对象级权限检查(前提是用了支持对象权限的后端,如django-guardian) - 默认 Django auth 后端不支持对象级权限,传了实例也无效——这点极易被忽略
- 如果只是检查“有没有 blog 这个 app 的任意权限”,用
user.has_module_perms('blog')更轻量
用 django-guardian 实现真正的对象级权限
这是目前最成熟、侵入性最小的对象权限方案。它通过外键关联 User/Group 和具体模型实例,把权限存到独立表里。
- 安装后必须在
settings.py中替换认证后端:AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend', 'guardian.backends.ObjectPermissionBackend'] - 给模型加
GuardedModel父类或直接用assign_perm('blog.change_post', user, post)赋权 - 模板里可用
{% if perms.blog.change_post %}做简单判断,但对象级权限需配合get_objects_for_user查询可操作的数据集 - 注意:
get_objects_for_user(user, 'blog.change_post')返回的是 QuerySet,不是布尔值;别在模板里直接用它做 if 判断
对象级权限的复杂点不在怎么装库,而在于什么时候该查数据库、什么时候缓存结果、以及删除对象时是否同步清理权限记录——这些都得自己补。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











