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

如何用Django ORM筛选用户拥有全部允许组权限的文档

筛选Person可访问的所有Document(需满足属于文档所有允许组)

模型定义

class Document(models.Model):
    allowed_groups = models.ManyToManyField(Group, related_name='allowed_documents')

class Person(models.Model):
    permission_groups = models.ManyToManyField(Group, related_name='people')

class Group(models.Model):
    id = models.BigIntegerField()

需求说明

需要筛选出某个Person可访问的所有Document,条件是该Person必须属于该Document的所有allowed_groups成员。

测试场景

  • 场景1:Document(allowed_groups=1,2,7) 对应 Person(permission_groups=1,2,6,7,11,15) → 匹配
  • 场景2:Document(allowed_groups=1,2,7) 对应 Person(permission_groups=1,7) → 不匹配
  • 场景3:Document(allowed_groups=1,2,7) 对应 Person(permission_groups=1,2) → 不匹配
  • 场景4:Document(allowed_groups=1,2,7) 对应 Person(permission_groups=2) → 不匹配
  • 场景5:Document(allowed_groups=1,2,7) 对应 Person(permission_groups=8) → 不匹配
  • 场景6:Document(allowed_groups=1,2,7) 对应 Person(permission_groups=1,2,7) → 匹配

现有代码问题

使用以下代码会匹配除场景5外的所有情况,不符合需求:

person = Person.objects.get(pk=1)
Document.objects.filter(allowed_groups__in=person.permission_groups.all())

解决方案

通过Django ORM的annotate和Count结合筛选,核心逻辑是:计算每个文档的允许组总数,同时计算该文档允许组中用户拥有的数量,当两者相等时,说明用户拥有该文档所有允许组。

from django.db.models import Count, Q

person = Person.objects.get(pk=1)
user_group_ids = person.permission_groups.values_list('id', flat=True)

# 可链式添加其他filter条件
documents = Document.objects.annotate(
    total_allowed_groups=Count('allowed_groups'),
    matched_groups=Count(
        'allowed_groups',
        filter=Q(allowed_groups__id__in=user_group_ids)
    )
).filter(total_allowed_groups=matched_groups)

逻辑说明

  1. total_allowed_groups:统计每个文档关联的允许组总数
  2. matched_groups:统计每个文档的允许组中,用户拥有的组数量
  3. 最后筛选total_allowed_groups=matched_groups的文档,确保用户拥有该文档的所有允许组

这种写法可以直接和其他filter/Q对象链式调用,无需手写SQL。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 08:01:26