The Queries an ORM Writes

Now the Django course makes sense. Every ORM call becomes SQL, and you can now read it.

Post.objects.filter(published=True) becomes SELECT ... WHERE published = 1.

.select_related("author") adds a JOIN.

.prefetch_related("tags") runs a second query and matches the results in Python, because a join cannot do many-to-many without multiplying rows.

That last one is not a Django quirk. It is a direct consequence of module 5: joins multiply rows.

The N+1 problem, in SQL

N+1

-- what a loop over
-- post.author produces
SELECT * FROM posts;
SELECT * FROM authors
  WHERE id = 1;
SELECT * FROM authors
  WHERE id = 2;
-- once per post

One query

-- what select_related
-- produces instead
SELECT p.*, a.name
FROM posts p
JOIN authors a
  ON a.id = p.author_id;
Compare → Fifty-one queries against two. Seeing it as SQL is what turns select_related from a rule you memorized into something obvious.

Two ORM behaviors that surprise people, both explicable now.

.update() on a queryset runs one UPDATE statement and does not call save() or fire signals, because it never loads the objects at all. That is why it is fast and why your custom save() logic is skipped.

A queryset is lazy because SQL is: nothing is sent to the database until something needs the rows.

Mark this lesson complete

Signed in, your progress follows you to every device.

Next: When to Drop to SQL →
← Back