For the sake of simplicity let's say I only have 2 models: Book, Author
class Author(models.Model):
name = models.CharField(max_length='100')
...
class Book(models.Model):
name = models.CharField(max_length='100')
authors = models.ManyToManyField(Author)
...
I want to filter Books using a list of authors. What I tried to do is:
authors = [...] # a list of author objects
Books.objects.filter(authors__in=authors)
But here, the authors will be ORed when I want them ANDed. Is there any way to AND many-to-many filtering??
You could & together a bunch of Q objects:
q = Q()
for author in authors:
q &= Q(authors=author)
Books.objects.filter(q)
To exclude books that have authors outside the list, you could limit the query to books with exactly the number of authors in the list:
Books.objects.annotate(count=Count('authors')).filter(count=len(authors)).filter(q)
Update:
Based on comments, I think the requirement is to get all books authored by at least one author in the list, but exclude books with any authors outside the list.
So we build a queryset selecting the authors that we hate:
# this queryset will be embedded as a subquery in the next
bad_authors = Author.objects.exclude(name__in=['A1', 'A2'])
Then exclude them to find the books we want:
# get all books without any of the bad_authors
Books.objects.exclude(authors__in=bad_authors)
This will return all books except those authored by someone outside your list. If you also want to exclude those with no authors listed, add another exclude call:
Books.objects.exclude(authors__in=bad_authors).exclude(authors=None)
This will leave us with just the books authored by one or more of the good ones!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With