Search code examples
pythondjangodjango-viewsdjango-q

Filtering a many-to-many relationship with Django's Q query


I have a model

class Book(models.Model):

    title = models.CharField(max_length=200)
    authors = models.ManyToManyField(Author)

class Author(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

And I have a view

class SearchResultsListView(ListView):
    model = Book
    context_object_name = 'book_list'
    template_name = 'book/search_results.html'

    def get_queryset(self):
        query = self.request.GET.get('q')
        return Book.objects.filter(
            Q(title__icontains=query)
        )

I can't figure out how to access foreignkeys. How do I do a Q query searching for say, "show me only books in which any authors of the book that have the query string in any part of their last name"?


Solution

  • You can look "through" a relation with two consecutive underscores (__). So here you can filter on books where the last_name of the author contains a certain substring with:

    Book.objects.filter(
        authors__last_name__icontains=query
    )

    If you thus look for Books where the title contains the given query, or the last_name of one of the authors contains the query, then you can filter with:

    Book.objects.filter(
        Q(title__icontains=query) |
        Q(authors__last_name__icontains=query)
    )