Django Queryset 通过 Manytomany 过滤 pk 列表
Django Queryset filtering pk list over Manytomany
我想获取 Project.objects.all() 的查询集并过滤所有包含用户选择的属性列表的项目。
models.py
class Attribute(models.Model):
title = models.CharField(max_length=20)
class Project(models.Model):
title = models.CharField(max_length=30)
attributes = models.ManyToManyField(Attribute)
## For the sake of this example, there are two Projects and two Attributes
## Project1 is associated to attr1 and attr2
## Project2 is associated to attr1
我发现了这个:Django Docs Complex Lookups with Q objects 并通过其他 Whosebug 答案找到了这里:
非工作代码(self.multiselect 是 pks 列表)
query = reduce(operator.and_, (Q(attributes__pk=selection) for selection in self.multiselect))
return queryset.filter(query)
但是这段代码提供了一个空的查询集。我开始在交互式 shell 中进行一些测试以找出问题所在。
测试
queryset = Project.objects.all()
########## & is not working
queryset.filter(Q(attributes__pk=1) & Q(attributes__pk=2))
# []
########## | has no issues
queryset.filter(Q(attributes__pk=1) | Q(attributes__pk=2))
# [<Project: Project1>, <Project: Project2>, <Project: Project1>]
########## & works on Project but not on Attribute
queryset.filter(Q(title__contains="Project") & Q(title__contains="1"))
# [<Project: Project1>]
& 似乎没有处理项目和属性之间的多对多关系。这是否有原因,是否有一个简单的代码修复可以使其正常工作?任何帮助将不胜感激。
附带说明一下,reduce 函数返回的正是它应该返回的内容
########## Reduce does not create an issue
Q(attributes__pk=1) & Q(attributes__pk=2)
# <Q: (AND: ('attributes__pk', 1), ('attributes__pk', 2))>
reduce(operator.and_, [Q(attributes__pk=selection) for selection in [1,2]])
# <Q: (AND: ('attributes__pk', 1), ('attributes__pk', 2))>
我不明白为什么 filter
、Q
和 &
有效。我想过否定否定,这让我想到了这个解决方案。
Project.objects.exclude(~(Q(attributes__pk=1) & Q(attributes__pk=2)))
使用 Q
和 &
的排除和否定原始查询过滤器有效。
如你所说:
########## & is not working
queryset.filter(Q(attributes__pk=1) & Q(attributes__pk=2))
应该是:
attrs = Attribute.objects.filter(pk__in=self.multiselect)
Photo.objects.filter(attributes__in=attrs).annotate(num_attr=Count('attributes')).filter(num_attr=len(attrs))
看到这个:Django filter queryset __in for *every* item in list
我想获取 Project.objects.all() 的查询集并过滤所有包含用户选择的属性列表的项目。
models.py
class Attribute(models.Model):
title = models.CharField(max_length=20)
class Project(models.Model):
title = models.CharField(max_length=30)
attributes = models.ManyToManyField(Attribute)
## For the sake of this example, there are two Projects and two Attributes
## Project1 is associated to attr1 and attr2
## Project2 is associated to attr1
我发现了这个:Django Docs Complex Lookups with Q objects 并通过其他 Whosebug 答案找到了这里:
非工作代码(self.multiselect 是 pks 列表)
query = reduce(operator.and_, (Q(attributes__pk=selection) for selection in self.multiselect))
return queryset.filter(query)
但是这段代码提供了一个空的查询集。我开始在交互式 shell 中进行一些测试以找出问题所在。
测试
queryset = Project.objects.all()
########## & is not working
queryset.filter(Q(attributes__pk=1) & Q(attributes__pk=2))
# []
########## | has no issues
queryset.filter(Q(attributes__pk=1) | Q(attributes__pk=2))
# [<Project: Project1>, <Project: Project2>, <Project: Project1>]
########## & works on Project but not on Attribute
queryset.filter(Q(title__contains="Project") & Q(title__contains="1"))
# [<Project: Project1>]
& 似乎没有处理项目和属性之间的多对多关系。这是否有原因,是否有一个简单的代码修复可以使其正常工作?任何帮助将不胜感激。
附带说明一下,reduce 函数返回的正是它应该返回的内容
########## Reduce does not create an issue
Q(attributes__pk=1) & Q(attributes__pk=2)
# <Q: (AND: ('attributes__pk', 1), ('attributes__pk', 2))>
reduce(operator.and_, [Q(attributes__pk=selection) for selection in [1,2]])
# <Q: (AND: ('attributes__pk', 1), ('attributes__pk', 2))>
我不明白为什么 filter
、Q
和 &
有效。我想过否定否定,这让我想到了这个解决方案。
Project.objects.exclude(~(Q(attributes__pk=1) & Q(attributes__pk=2)))
使用 Q
和 &
的排除和否定原始查询过滤器有效。
如你所说:
########## & is not working
queryset.filter(Q(attributes__pk=1) & Q(attributes__pk=2))
应该是:
attrs = Attribute.objects.filter(pk__in=self.multiselect)
Photo.objects.filter(attributes__in=attrs).annotate(num_attr=Count('attributes')).filter(num_attr=len(attrs))
看到这个:Django filter queryset __in for *every* item in list