Skip to content

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() or len() 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>]>