Q Expressions¶
Q gives you composable query expressions for complex predicates.
Use it when you need nested boolean logic that is harder to express with chained
filter()/or_()/not_() calls.
Basic Usage¶
from saffier import Q
query = User.query.filter(Q(name="Adam") & Q(email__icontains="saffier"))
users = await query
OR and NOT¶
from saffier import Q
# name is Adam OR Saffier
query = User.query.filter(Q(name="Adam") | Q(name="Saffier"))
# NOT name is Adam
query = User.query.filter(~Q(name="Adam"))
Nested Expressions¶
from saffier import Q
query = User.query.filter((Q(name="Adam") & Q(email__icontains="dev")) | Q(name="Ravyn"))
Related Lookups¶
Q supports the same related lookups used by queryset kwargs.
from saffier import Q
query = Product.query.filter(Q(user__email__icontains="saffier"))
products = await query
The same lookup rules also work across forward and reverse many-to-many paths.
from saffier import Q
query = User.query.filter(
Q(products__name__icontains="soap") | Q(products__categories__name="food")
).distinct("id")
users = await query
Reverse foreign-key paths and embedded-parent relationship paths can be composed in the same way.
from saffier import Q
albums = await Album.query.filter(Q(tracks_set__title__icontains="bird")).distinct("id")
tracks = await Track.query.filter(
Q(studio__album__company__name="Acme") | Q(album__name__icontains="live")
)
Mixing Raw SQLAlchemy Clauses¶
You can combine Q with SQLAlchemy column expressions.
from saffier import Q
query = User.query.filter(Q(User.columns.name == "Adam") & Q(email__icontains="saffier"))
Using Q with or_()¶
from saffier import Q
query = User.query.or_(Q(name="Adam")).or_(Q(name="Saffier"))
users = await query