利用django-guardian为Django Admin实现行级权限控制

竹里
竹里
发布时间:2026-10-13 | 5人浏览过
原创

本文介绍如何在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原生权限系统那样提供开箱即用的体验,目前仍需较多定制开发。

本文内容旨在为读者提供学习参考,欢迎支持本站。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表