Filtering
The .filter() method is your primary tool for querying. It translates Python arguments into SQL WHERE clauses.
Basic Syntax
# Exact match (lookup name is implicit)
await Post.objects.filter(active=True)
await Post.objects.filter(author_id=5)
# With a lookup
await Post.objects.filter(views__gt=100)
The pattern is field_name__lookup=value. When no lookup is specified, exact is used.
Comparison Lookups
Post.objects.filter(views__gt=100) # views > 100
Post.objects.filter(views__gte=100) # views >= 100
Post.objects.filter(views__lt=50) # views < 50
Post.objects.filter(views__lte=50) # views <= 50
String Lookups
Post.objects.filter(title__contains="Python") # LIKE '%Python%'
Post.objects.filter(title__icontains="python") # LIKE '%python%' (case-insensitive)
Post.objects.filter(title__startswith="How") # LIKE 'How%'
Post.objects.filter(title__istartswith="how") # LIKE 'how%' (case-insensitive)
Post.objects.filter(title__endswith="guide") # LIKE '%guide'
Post.objects.filter(title__iendswith="Guide") # LIKE '%guide' (case-insensitive)
Null Checks
Post.objects.filter(body__isnull=True) # body IS NULL
Post.objects.filter(body__isnull=False) # body IS NOT NULL
Membership
# IN clause
Post.objects.filter(status__in=["draft", "published"])
Post.objects.filter(id__in=[1, 2, 3, 4, 5])
Range
# BETWEEN clause
Post.objects.filter(views__range=(100, 1000))
Post.objects.filter(created_at__range=(start_date, end_date))
Multiple Filters
Multiple filters are AND-ed together:
# WHERE active = true AND views > 100
Post.objects.filter(active=True, views__gt=100)
Excluding
# WHERE NOT (status = 'draft')
Post.objects.exclude(status="draft")
# Chain filter + exclude
Post.objects.filter(active=True).exclude(title__startswith="Draft")
Built-in Lookups Reference
| Lookup | SQL | Example |
|---|---|---|
exact | col = ? | filter(title="Hello") |
gt | col > ? | filter(views__gt=100) |
gte | col >= ? | filter(views__gte=100) |
lt | col < ? | filter(views__lt=50) |
lte | col <= ? | filter(views__lte=1000) |
contains | col LIKE ? | filter(title__contains="Py") |
icontains | LOWER(col) LIKE LOWER(?) | filter(title__icontains="py") |
startswith | col LIKE ? | filter(title__startswith="How") |
istartswith | LOWER(col) LIKE LOWER(?) | filter(title__istartswith="how") |
endswith | col LIKE ? | filter(title__endswith="guide") |
iendswith | LOWER(col) LIKE LOWER(?) | filter(title__iendswith="Guide") |
isnull | col IS NULL / IS NOT NULL | filter(body__isnull=True) |
in | col IN (?, ?, ...) | filter(id__in=[1,2,3]) |
range | col BETWEEN ? AND ? | filter(views__range=(100,1000)) |
Custom Lookups
Register your own SQL lookups:
import ryx
# Postgres ILIKE
ryx.register_lookup("ilike", "{col} ILIKE ?")
# Usage
Post.objects.filter(title__ilike="%python%")
# Decorator style
@ryx.lookup("uuid_prefix")
def uuid_prefix_lookup(field, value):
"""{col}::text LIKE ?"""
Next Steps
→ Q Objects — OR and NOT expressions → Custom Lookups — Advanced lookup patterns