我有两个简单的模型。让我们说标签和帖子。它们看起来像这样(简化):
class Tag(models.Model):
name = models.CharField(max_length=255, blank=False, unique=True)
class Post(models.Model):
title = models.CharField(max_length=255, blank=False, default='')
tags = models.ManyToManyField(Tag)
因此,每个标签可以分配到n个帖子,反之亦然。
我正在尝试获取所有帖子的列表,其中分配了任何给定标签,或者分配了所有给定标签。
所以基本上我想要OR或AND组合。
OR非常简单。在我看来我做(假设“标签”是标签对象的列表)
queryset = Post.objects.filter(tags__in=tags)
但我无法弄清楚如何为AND组合做到这一点。我要么得到任何东西,要么与OR一样。我尝试了很多不同的东西,但在我的场景中没有任何效果,我有一个动态的标签列表来过滤。
我最有希望的方法看起来像这样:
filter = [Q(tags=tag) for tag in tags if tag.enabled == True]
qs = Post.objects.all()
qs = qs.filter(reduce(operator.__and__, filter))
这仍然返回一个空列表。是的,我100%确定我有Post记录,并为其分配了两个请求的标签。
我究竟做错了什么?
好的,答案是,像往常一样,相对简单:
qs = Post.objects.all()
for tag in tags:
qs = qs.filter(tags=tag)
为了实现所需的过滤器,我们可以这样做:
wanted_tags = [tag_1, tag_2, tag_3,...]
# To keep it as lean as possible, we first minimise our search space by filtering solely the posts with the amount of tags in the first place:
preprocessed_posts = Post.objects.annotate(c=Count('tags')).filter(c=len(wanted_tags))
# Now comes the somewhat pythonic part, which is simple but feels kinda hacky:
for tag in wanted_tags:
preprocessed_posts.filter(tags=tag)
# now preprocessed_posts contains the wanted entities
wanted_posts = preprocessed_posts