Chaining & Lazy QuerySets
The animal list on our site is going to be built in steps: the animals a visitor can see, then the ones that match the visitor's search. We need to build a query a piece at a time, and run it once.
Chaining Filters
filter() and order_by() each return a new QuerySet, so we can call another method on the result:
Python REPL
>>> cats = Animal.objects.filter(species="cat")
>>> available_cats = cats.filter(status="available").order_by("name")
available_cats is the cats that are available, sorted by name. cats doesn't change.
When a Query Runs
Neither of those lines ran a query. A QuerySet is lazy: it waits until something needs its rows. Until then, its query attribute shows the SQL it's going to send:
Python REPL
>>> print(available_cats.query)
SELECT ... FROM "animals_animal" WHERE ("animals_animal"."species" = cat AND "animals_animal"."status" = available) ORDER BY "animals_animal"."name" ASC
The two filters and the ordering became one SQL statement, with AND between the conditions.
A QuerySet runs its query when its rows are needed, such as when we:
- loop over it
- call
list()orlen()on it - test it in an
if - print it in the shell
Printing is what ran our queries so far:
Python REPL
>>> available_cats
<QuerySet [<Animal: Peanut>]>