利用django-guardian为Django Admin实现行级权限控制
本文介绍如何在Django Admin中通过django-guardian实现细粒度的行级权限控制。针对内置权限无法覆盖数据行级别的痛点,通过继承GuardedModelAdmin并重写has_module_permission、get_queryset等关键方法,解决新用户无访问权限的问题。文章提供了完整的代码修改示例及配置步骤,帮助开发者高效构建安全的管理后台。
Django框架在构建后台管理Web应用时具有显著优势,其内置的模块级权限系统足以应对大多数内部系统的需求,能大幅降低开发成本。然而,Django原生权限机制并不支持行级(对象级)权限控制。若需实现更精细的数据访问限制,开发者必须依赖第三方应用或自行开发相关功能。
需求背景
项目组以往使用MySQL数据库存储系统配置,但手动修改数据库既繁琐又易出错。利用Django Admin根据Model自动生成页面并提供权限管理的功能,我们将部分配置迁移至Django中。随着系统使用范围扩大,从项目组内部扩展至其他团队,原有的粗粒度权限已无法满足需求,因此亟需在现有系统中引入行级权限控制机制。
方案选型
虽然可以自行开发权限系统,但成本高且质量难以保证。经查阅Django Packages上的第三方权限解决方案列表(https://djangopackages.org/grids/g/perms/),django-guardian因其高人气、对行级权限的支持以及与Django Admin的良好整合能力,成为首选方案。
实施步骤
安装与配置django-guardian
django-guardian的安装配置相对简单,遵循官方文档操作即可。安装完成后,系统会自动在数据库中创建两张用于存储权限关系的表。
集成django-guardian至Django Admin
首先,修改admin.py文件中涉及行级权限的类定义,将父类由admin.ModelAdmin改为GuardedModelAdmin。保存后,在数据行详情页右上角“历史”按钮旁会出现“编辑对象权限”按钮,点击即可对该行数据的具体权限进行配置。
完成基础权限配置后,使用新用户测试会发现其无法访问任何数据。这是因为GuardedModelAdmin并未完全处理Admin页面的显示逻辑,需手动重写部分函数以正确展示数据。具体实现参考下方代码注释说明。
from guardian.admin import GuardedModelAdmin
from guardian.shortcuts import get_objects_for_user, assign_perm, remove_perm, get_users_with_perms, \
get_groups_with_perms
# 需改前
@admin.register(DataAssistantJob)
class DataAssistantJobAdmin(admin.ModelAdmin):
pass
# 修改后
@admin.register(DataAssistantJob)
class DataAssistantJobAdmin(GuardedModelAdmin):
# app是否在主页面中显示的话由该函数决定
def has_module_permission(self, request):
if super().has_module_permission(request):
return True
return self.get_model_objs(request).exists()
# 在显示数据列表额时候,哪些数据显示,哪些不显示,由该函数控制
def get_queryset(self, request):
if request.user.is_superuser:
return super().get_queryset(request)
data = self.get_model_objs(request)
return data
# 内部用来获取某个用户有权限访问的数据行
def get_model_objs(self, request, action=None, klass=None):
opts = self.opts
actions = [action] if action else ['view', 'change', 'delete']
klass = klass if klass else opts.model
model_name = klass._meta.model_name
return get_objects_for_user(user=request.user, perms=[f'{perm}_{model_name}' for perm in actions],
klass=klass, any_perm=True)
# 用来判断某个用户是否有某个数据行的权限
def has_perm(self, request, obj, action):
opts = self.opts
codename = f'{action}_{opts.model_name}'
if obj:
return request.user.has_perm(f'{opts.app_label}.{codename}', obj)
else:
return self.get_model_objs(request, action).exists()
# 是否有查看某个数据行的权限
def has_view_permission(self, request, obj=None):
return self.has_perm(request, obj, 'view')
# 是否有修改某个数据行的权限
def has_change_permission(self, request, obj=None):
return self.has_perm(request, obj, 'change')
# 是否有删除某个数据行的权限
def has_delete_permission(self, request, obj=None):
return self.has_perm(request, obj, 'delete')
# 用户应该拥有他新增的数据行的所有权限
def save_model(self, request, obj, form, change):
result = super().save_model(request, obj, form, change)
if not request.user.is_superuser and not change:
opts = self.opts
actions = ['view', 'add', 'change', 'delete']
[assign_perm(f'{opts.app_label}.{action}_{opts.model_name}', request.user, obj) for action in actions]
return result
经过上述修改,Django Admin模块已支持行级权限控制,并能正确在后台界面展示。若需为多个模块添加此功能,建议将相关修改封装至新基类中,其他模块通过继承该基类即可复用代码。
总结
django-guardian与Django Admin的整合效果尚待优化。对于不熟悉Django内部源码的开发者而言,实现行级权限控制仍较为繁琐。理想状态下,django-guardian应像Django原生权限系统那样提供开箱即用的体验,目前仍需较多定制开发。
本文内容旨在为读者提供学习参考,欢迎支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









