← all lessons

dailyfeed · 2026-04-27 · djangoormqueries

Q objects

The idea

filter(a=1, b=2) always means a=1 AND b=2. There's no keyword-argument syntax for OR. To write OR, NOT, or anything more than "all of these", wrap each clause in Q(...) and combine them with Python's bitwise operators:

You can mix Q objects with regular keyword arguments in the same .filter() call, but the Q objects must come first.

How it shows up

crawl_feeds needs feeds where next_check_at is either unset or in the past:

from django.db.models import Q

UserFeed.objects.filter(
    Q(next_check_at__isnull=True) | Q(next_check_at__lte=current)
)

Without Q, you'd run two queries and join them in Python — slower and clumsier.

Read more

Exercises

  1. Negate it — write a queryset that finds feeds that have errored but not yet recovered (last_error_at set AND last_success_at either NULL or earlier than last_error_at). Done when: the queryset runs in manage.py shell without error and returns the feeds you expect.
  2. Mix Q with kwargs — combine Q(...) | Q(...) with a user=request.user kwarg in the same .filter(). Confirm the kwarg ANDs with the OR group. Done when: you can articulate the order-of-operations rule: "Q objects first, kwargs second; kwargs are ANDed onto whatever the Q expression resolves to."

My notes