9

为简单起见,假设我只有 2 个模型:书籍、作者

class Author(models.Model):
    name = models.CharField(max_length='100')
    ...

class Book(models.Model):
    name = models.CharField(max_length='100')
    authors = models.ManyToManyField(Author)
    ...

我想使用作者列表过滤书籍。我试图做的是:

authors = [...] # a list of author objects
Books.objects.filter(authors__in=authors)

但是在这里,当我想要他们与作者时,他们将被 ORed。有什么方法可以进行多对多过滤吗?

4

1 回答 1

15

您可以将一堆 Q 对象 & 在一起:

q = Q()
for author in authors:
    q &= Q(authors=author)
Books.objects.filter(q)

要排除作者不在列表中的书籍,您可以将查询限制为与列表中作者数量完全相同的书籍:

Books.objects.annotate(count=Count('authors')).filter(count=len(authors)).filter(q)

更新:

根据评论,我认为要求是获得列表中至少一位作者撰写的所有书籍,但排除列表之外任何作者的书籍。

所以我们构建了一个查询集来选择我们讨厌的作者:

# this queryset will be embedded as a subquery in the next
bad_authors = Author.objects.exclude(name__in=['A1', 'A2'])

然后排除它们以找到我们想要的书籍:

# get all books without any of the bad_authors
Books.objects.exclude(authors__in=bad_authors)

这将返回所有书籍,但由您列表之外的人创作的书籍除外。如果您还想排除那些没有列出作者的人,请添加另一个排除调用:

Books.objects.exclude(authors__in=bad_authors).exclude(authors=None)

这将使我们只剩下一本或多本好书所写的书!

于 2012-10-05T19:44:34.063 回答