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

Django中如何反向使用exclude内的sub_categories__role__in查询条件?

问题描述

我定义了如下SettingMenus模型:

class SettingMenus(models.Model):
    cat = models.ForeignKey(Setting, on_delete=models.CASCADE, related_name="menus", null=True)
    index = models.SmallIntegerField(validators=[MaxValueValidator(32766)], default=0)
    name = models.CharField(max_length=100, null=True)
    path = models.CharField(max_length=200, null=True, blank=True)
    parent = models.ForeignKey('self', null=True, blank=True, related_name='sub_categories', on_delete=models.CASCADE)
    role = models.ManyToManyField(UserRole, related_name="settingMenus", blank=True)
    tables = models.ManyToManyField(AppModels, related_name="setting_menus", blank = True,)

视图中原有查询语句:

nv1menus = SettingMenus.objects.filter(
    cat__company__name=company,
    cat__application="common",
    index=0,
    role__in=request.user.groups.all()
).exclude(sub_categories__role__in=request.user.groups.all()).distinct()

我需要实现与exclude(sub_categories__role__in=request.user.groups.all())相反的逻辑——筛选出存在满足用户角色的子菜单的记录,而非排除这类记录。尝试过sub_categories__role__in!=request.user.groups.all()和not sub_categories__role__in=request.user.groups.all(),但都无法生效。

更新说明

UserRole、UserAccess模型分别继承自django.contrib.auth.models中的Group和Permission,具体定义如下:

class UserRole(Group, CommonModel):
    description = models.TextField(blank=True, null=True, verbose_name="Group Description")

    def __str__(self):
        return self.name

class UserAccess(Permission, CommonModel):
    description = models.CharField(max_length=255, null=True, blank=True, verbose_name="Permission Description")

    def __str__(self):
        return self.name

class User(AbstractUser):
    id = models.BigAutoField(primary_key=True)
    mobile_number = models.CharField(max_length=15, null=True, blank=True)
    bio = models.TextField(verbose_name="About Me", null=True, blank=True)

    groups = models.ManyToManyField(
        UserRole,
        verbose_name="User Groups",
        blank=True,
        related_name="users",
        related_query_name="user"
    )

    user_permissions = models.ManyToManyField(
        UserAccess,
        verbose_name="User Permissions",
        blank=True,
        related_name="users",
        related_query_name="user"
    )

    def __str__(self):
        return self.username
解决方案

要筛选出存在至少一个子菜单拥有用户所属角色的SettingMenus记录,直接将原exclude中的条件移到filter内即可,Django的正向关联查询sub_categories__role__in本身就表示“该记录存在子菜单,且子菜单的角色包含用户的任意一个角色”。

修改后的查询语句:

nv1menus = SettingMenus.objects.filter(
    cat__company__name=company,
    cat__application="common",
    index=0,
    role__in=request.user.groups.all(),
    sub_categories__role__in=request.user.groups.all()
).distinct()

补充说明

  • 原exclude是排除有符合条件子菜单的记录,将该条件移入filter后,就实现了反向筛选逻辑。
  • 必须保留.distinct():一个父菜单可能对应多个符合条件的子菜单,会导致查询结果重复,distinct()用于去重。
  • 如果需要同时包含“无子女菜单”的记录,可使用Q查询扩展:
from django.db.models import Q

nv1menus = SettingMenus.objects.filter(
    cat__company__name=company,
    cat__application="common",
    index=0,
    role__in=request.user.groups.all(),
    Q(sub_categories__isnull=True) | Q(sub_categories__role__in=request.user.groups.all())
).distinct()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 06:22:40