You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django使用django-guardian按关联对象权限过滤Queryset实现方案

问题1:关联字段的检测与管理器获取

你可以通过Django模型自带的_meta元数据接口遍历所有字段,筛选出正向关联字段(外键、一对一、多对多),不需要依赖queryset,直接用当前管理器绑定的self.model就能获取:

  • 调用self.model._meta.get_fields()拿到模型所有字段
  • 筛选满足field.is_relation且field.auto_created == False的字段,就是当前模型主动定义的正向关联字段
  • 关联字段对应的模型可以通过field.related_model获取,其管理器就是field.related_model.objects

问题2:递归校验多层关联对象权限的实现

我们可以在自定义Manager里加递归逻辑,遍历所有关联层级,逐层添加权限过滤条件,跨表查询用Django的双下划线语法关联到对应层级的权限判断即可,修改后的完整代码如下:

from django.db import models
from guardian.shortcuts import get_objects_for_user

class WithUser(models.Manager):
    user_obj = None
    # 缓存已处理过的模型,避免循环关联导致死递归
    _processed_models = set()

    def assign_user(self, user_obj):
        self.user_obj = user_obj
        # 每次赋值用户清空处理缓存,避免不同请求串数据
        self._processed_models = set()
        return self

    def _get_perm_filtered_pks(self, model, user):
        """获取指定模型下用户有查看权限的主键列表"""
        perm_codename = f"view_{model.__name__.lower()}"
        return get_objects_for_user(
            user, perm_codename, model.objects.all(), accept_global_perms=False
        ).values_list("pk", flat=True)

    def _recursive_add_perm_filter(self, qs, current_model, prefix=""):
        """递归添加当前模型和所有关联模型的权限过滤"""
        if current_model in self._processed_models:
            return qs
        self._processed_models.add(current_model)

        # 先过滤当前模型自身的权限
        current_pks = self._get_perm_filtered_pks(current_model, self.user_obj)
        pk_field = f"{prefix}pk" if prefix else "pk"
        qs = qs.filter(**{f"{pk_field}__in": current_pks})

        # 遍历所有正向关联字段,处理下一层
        for field in current_model._meta.get_fields():
            if field.is_relation and not field.auto_created:
                related_model = field.related_model
                new_prefix = f"{prefix}{field.name}__"
                qs = self._recursive_add_perm_filter(qs, related_model, new_prefix)
        
        return qs

    def get_queryset(self):
        qs = super().get_queryset()
        if self.user_obj:
            qs = self._recursive_add_perm_filter(qs, self.model)
        return qs

# 模型定义和原有逻辑一致,不需要修改
class A(models.Model):
    # 自定义字段
    objects = WithUser()

class B(models.Model):
    # 自定义字段
    a = models.ForeignKey(A, on_delete=models.CASCADE)
    objects = WithUser()

class C(models.Model):
    # 自定义字段
    b = models.ForeignKey(B, on_delete=models.CASCADE)
    objects = WithUser()

使用示例

# 假设当前登录用户为user,查询用户有权限的C对象,会自动校验C、B、A三层的view权限
c_qs = C.objects.assign_user(user).all()

补充说明

如果不需要对所有关联字段做权限校验,也可以在模型里新增自定义属性比如permission_related_fields指定需要校验的关联字段,遍历的时候只处理指定字段即可,灵活度更高。

内容的提问来源于stack exchange,提问作者AlASAD WAIL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 23:09:01