Challenge 2: Query Across a Many-to-Many — Possible Solution ==================================================================== from catalog.models import Book scifi_or_fantasy_books = Book.objects.filter( tags__name__in=["sci-fi", "fantasy"] ).distinct() # Equivalent using Q objects, for combining conditions more explicitly: from django.db.models import Q scifi_or_fantasy_books_q = Book.objects.filter( Q(tags__name="sci-fi") | Q(tags__name="fantasy") ).distinct() WHY .distinct() IS NECESSARY HERE ------------------------------------ A book tagged with BOTH "sci-fi" and "fantasy" would otherwise appear TWICE in the result set. Here's why: filtering across a ManyToMany field causes Django to generate SQL that joins the Book table to the hidden join table (and then to Tag) — for a book matching the filter through two different tag rows, the join produces two separate result rows for that one book, one per matching tag. .distinct() removes those duplicate rows before the QuerySet is returned, so each qualifying book appears exactly once regardless of how many of its tags matched the filter. WHY THIS WORKS -------------- - tags__name__in=["sci-fi", "fantasy"] spans the ManyToManyField (tags) and reaches into the related Tag model's `name` field, using the __in lookup to match either of two values — a shorthand for "tags__name='sci-fi' OR tags__name='fantasy'" without writing an explicit Q object OR expression. - The Q object version demonstrates the same result built more explicitly with the | (OR) operator — useful once conditions get more complex than a simple __in check (e.g. mixing AND and OR conditions across different fields). - .distinct() is specifically needed for relationship-spanning filters on a to-many relationship (ManyToMany or reverse ForeignKey) — filtering on the "one" side of a plain ForeignKey (like Chapter 6's author__name filter) never produces this duplication, since each book has exactly one author.