In a Django queryset, the double underscore is not just a Python-name oddity — it's a mini query DSL. field__lookup=value translates to a SQL operator:
| Lookup | SQL |
|---|---|
__exact (default) |
= |
__lt, __lte, __gt, __gte |
<, <=, >, >= |
__in |
IN (...) |
__isnull |
IS NULL / IS NOT NULL |
__icontains, __istartswith |
LIKE '%...%', case-insensitive |
__date, __year, __month |
DATE(col) = ..., etc. |
__regex, __iregex |
regex match |
Double underscore also crosses ForeignKey relations: Story.objects.filter(feed__user=request.user) joins through feed to filter on the related user.
This PR uses three different lookups in close quarters:
Q(next_check_at__isnull=True) | Q(next_check_at__lte=current) # IS NULL / <=
Story.objects.filter(feed__user=user, published_at__date=digest_date) # join + DATE()
The combination of join-traversal + date-truncation in one filter is what makes the ORM feel powerful — the SQL underneath is non-trivial.
manage.py shell, and print str(qs.query) to see the generated SQL. Note which lookups produced which clauses.
Done when: you've matched at least three __lookups to their SQL counterparts in your own code.UserFeeds belonging to staff users (user__is_staff=True).
Done when: the queryset returns the right rows in shell.