Django Admin中如何配置权限实现用户仅编辑自有模型记录
Django Admin 实现用户内容访问/编辑隔离的原生方案
所有实现全程依赖Django自带Admin的内置钩子,无需开发独立管理面板、无需引入第三方权限包。
第一步:模型层补全内容归属关联
所有需要做隔离的业务模型,必须新增指向内置User模型的外键字段,用来标记内容所有者,字段设置为不可在Admin表单直接编辑,避免用户篡改归属。
示例模型代码:
from django.db import models from django.contrib.auth import get_user_model User = get_user_model() class Post(models.Model): title = models.CharField(max_length=100) content = models.TextField() created_at = models.DateTimeField(auto_now_add=True) # 内容归属字段 author = models.ForeignKey( User, on_delete=models.CASCADE, related_name="user_posts", editable=False ) class Comment(models.Model): post = models.ForeignKey(Post, on_delete=models.CASCADE) content = models.TextField() created_at = models.DateTimeField(auto_now_add=True) author = models.ForeignKey( User, on_delete=models.CASCADE, related_name="user_comments", editable=False )
第二步:重写ModelAdmin核心钩子完成隔离
针对每个需要隔离的模型,注册Admin配置类,重写以下几个内置方法实现全流程拦截:
- 重写
get_queryset:过滤所有用户可见的记录集,普通用户仅能拿到自己创建的内容,超管默认保留全量查看权限 - 重写
save_model:新建内容时自动绑定当前登录用户为作者,无需手动选择 - 重写对象级权限校验方法:在编辑、删除操作触发前,校验当前操作人是否为内容所有者,哪怕用户手动构造请求参数越权,也会被拦截
完整的ModelAdmin配置示例:
from django.contrib import admin from .models import Post, Comment @admin.register(Post) class PostAdmin(admin.ModelAdmin): list_display = ["title", "created_at", "author"] def get_queryset(self, request): qs = super().get_queryset(request) # 超管不做过滤,保留全量操作权限 if request.user.is_superuser: return qs # 普通用户仅返回自己创作的内容 return qs.filter(author=request.user) def save_model(self, request, obj, form, change): # 新建对象时自动绑定当前用户为作者 if not change: obj.author = request.user super().save_model(request, obj, form, change) def has_change_permission(self, request, obj=None): # 编辑权限兜底校验:操作具体对象时,必须是所有者才放行 if obj is not None and obj.author != request.user: return False return super().has_change_permission(request, obj) def has_delete_permission(self, request, obj=None): # 删除权限兜底校验 if obj is not None and obj.author != request.user: return False return super().has_delete_permission(request, obj) # Comment的Admin配置和Post逻辑完全一致,照搬上述重写方法即可 @admin.register(Comment) class CommentAdmin(admin.ModelAdmin): list_display = ["post", "created_at", "author"] def get_queryset(self, request): qs = super().get_queryset(request) if request.user.is_superuser: return qs return qs.filter(author=request.user) def save_model(self, request, obj, form, change): if not change: obj.author = request.user super().save_model(request, obj, form, change) def has_change_permission(self, request, obj=None): if obj is not None and obj.author != request.user: return False return super().has_change_permission(request, obj) def has_delete_permission(self, request, obj=None): if obj is not None and obj.author != request.user: return False return super().has_delete_permission(request, obj)
如果多个模型都需要实现相同的隔离逻辑,可以把上述重写方法抽成通用Mixin类,注册Admin时直接继承即可,避免重复编写代码。
第三步:分配普通用户的Admin基础权限
不需要给普通用户开超级管理员权限,通过Django内置的权限组机制分配权限即可:
- 在Admin的「认证和授权」分类下新建权限组,比如命名为「内容创作者」
- 给该组勾选对应模型的
view/add/change基础权限(如果需要允许普通用户删除自己的内容,再加delete权限) - 把所有需要登录Admin管理自有内容的普通用户,加入这个权限组即可
落地注意事项
- 隔离逻辑必须做两层拦截:查询集过滤负责列表/详情页的可见范围,对象级权限校验负责操作环节的兜底,不能只靠前端隐藏按钮实现隔离
- 代码中的超管分支必须保留,否则超级管理员也会被限制为仅能查看自己的内容,影响日常运维
- 归属字段必须设置
editable=False,避免用户在表单页手动篡改内容归属人 - 列表页的批量操作功能会自动基于过滤后的查询集生效,普通用户即使手动构造请求传入其他用户的内容ID,也会因为查询集匹配不到记录被拦截
内容的提问来源于stack exchange,提问作者Pöy Shadman
相关产品推荐
相关产品推荐

