Skip to main content

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

LookupSQLExample
exactcol = ?filter(title="Hello")
gtcol > ?filter(views__gt=100)
gtecol >= ?filter(views__gte=100)
ltcol < ?filter(views__lt=50)
ltecol <= ?filter(views__lte=1000)
containscol LIKE ?filter(title__contains="Py")
icontainsLOWER(col) LIKE LOWER(?)filter(title__icontains="py")
startswithcol LIKE ?filter(title__startswith="How")
istartswithLOWER(col) LIKE LOWER(?)filter(title__istartswith="how")
endswithcol LIKE ?filter(title__endswith="guide")
iendswithLOWER(col) LIKE LOWER(?)filter(title__iendswith="Guide")
isnullcol IS NULL / IS NOT NULLfilter(body__isnull=True)
incol IN (?, ?, ...)filter(id__in=[1,2,3])
rangecol 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