我正在使用一个包含ManyToMany
字段brand_groups
的查询集。用户只能根据其组织访问BrandGroups
的子集。我正在尝试过滤ManyToMany
字段,同时仍然使用values()
,该字段与视图高度集成。
我正在使用的简化表:
class BrandGroup(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=256)
organization = models.ForeignKey(
Organization, related_name="brand_groups", null=False
)
class Fact(models.Model):
id = models.CharField(max_length=256, primary_key=True)
brand_groups = models.ManyToManyField(BrandGroup, blank=True)
过去为我工作的是使用Prefetch
对象来处理这种限制:
qs = Fact.objects.prefetch_related(
Prefetch("brand_groups",
queryset=BrandGroup.objects.filter(
organization_id=self.request.META["ORG_ID_HEADER"]
)))
但是我发现values()
似乎完全忽略了预取。
qs.values("brand_groups__name")
以上内容始终包含完整的关联BrandGroup
对象,但不包含过滤器。
我尝试将to_attr='org_brand_groups'
添加到Prefetch
,但是qs.values("org_brand_groups__name")
抱怨该字段不存在。
我还尝试过使用注释以类似的方式重命名预取字段。我没有抱怨该字段不存在,但是再次values()
返回了未过滤的查询集。
我设法完成这种过滤的唯一方法是使用子查询:
qs = Fact.objects.annotate(
brand_group_name=Subquery(
BrandGroup.objects.filter(
organization_id=self.request.META["ORG_ID_HEADER"],
Q(id=OuterRef("brand_groups__id"))).values(
"name"[:1],output_field=CharField(),))
# Now it gives me the desired results
qs.values("brand_group_name")
但是这种方法否定了我要完成的工作。目标是使用联接而不是子查询来提取BrandGroup
数据。
是否有任何方法可以过滤ManyToMany
相关字段而没有可以与values()
一起使用的子查询?我唯一剩下的想法是在已经应用values()
之后使用Python过滤查询集。
答案 0 :(得分:0)
我认为您误解了prefetch_related
的作用:
另一方面,prefetch_related对每个关系进行单独的查找,并在Python中执行“加入”。除了select_related支持的外键和一对一关系之外,这还允许它预取多对多和多对一对象,这不能使用select_related完成。
您最好的选择是使用示例中的子查询。
答案 1 :(得分:0)
最终,我最终没有进行任何过滤,而是在调用values
之后使用Exists
和使用values
过滤器之前。
qs = Fact.objects.filter(...)
qs = qs.values("brand_groups__name", "brand_groups__id")
qs = qs.annotate(
brand_group_accessible=Exists(
BrandGroup.objects.filter(
organization_id=self.request.META["ORG_ID_HEADER"],
Q(id=OuterRef("brand_groups__id")))))
qs = qs.filter(Q(brand_group_accessible=True) | Q(brand_groups__id__isnull=True))