Django Queryset过滤pk list over Manytomany

时间:2017-08-12 18:14:03

标签: python django python-3.x django-queryset django-1.9

我想采用Project.objects.all()的Queryset并过滤包含用户选择的属性列表的所有项目。

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并通过其他stackoverflow答案到达:

非工作代码(self.multiselect是pks列表)

query = reduce(operator.and_, (Q(attributes__pk=selection) for selection in self.multiselect))
return queryset.filter(query)

但是,此代码提供了一个空Queryset。我开始在交互式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>]

好像&amp;没有处理Project和Attribute之间的ManytoMany关系。是否有这样的原因,是否有一个简单的代码修复,使其正常工作?任何帮助将不胜感激。

作为旁注,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))>

2 个答案:

答案 0 :(得分:1)

正如你所说:

########## & 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

答案 1 :(得分:0)

我不明白为什么filterQ&有效。我想要否定这种否定,这使我得到了这个解决方案。

Project.objects.exclude(~(Q(attributes__pk=1) & Q(attributes__pk=2)))  

使用排除和取消Q&的原始查询过滤器。