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:
Q(a=1) | Q(b=2) → a=1 OR b=2Q(a=1) & Q(b=2) → a=1 AND b=2 (same as filter(a=1, b=2))~Q(a=1) → NOT (a=1)You can mix Q objects with regular keyword arguments in the same .filter() call, but the Q objects must come first.
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.
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.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."