2017-08-12 147 views
1

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

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并通过其他计算器得到的答案在这里:

非工作代码(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>] 

好像&不工作的项目和属性之间的多对多关系。是否有这个原因,是否有一个简单的修复代码,将使其正常工作?任何帮助,将不胜感激。


作为一个侧面说明,减少函数返回正是它应该

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

回答

0

我不明白为什么filterQ&作品。我想过否定否定,这导致我找到了这个解决方案。

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

使用排除与Q&否定原来的查询过滤器的工作。

+0

是啊。我试图找出背后的数学,但它似乎产生了所需的输出。谢谢<3编辑:我想必须有'过滤器','Q'和'&'的错误,因为否定给我的一切,但我的期望输出,所以你会认为,非否定形式会给我我预期的结果。很好,赶上^^ – pieisawesome102

+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

+0

使用attributes__in给出了所有选择属性__any__的项目列表,而不是选择属性__all__的项目 – pieisawesome102

+0

@ pieisawesome102'''annotate(num_attr = Count('attributes'))。filter(num_attr = len(attrs) )'''这确保选择了所有的属性。 – gushitong