Q!() { /* proc-macro */ }Expand description
Q!() — Django-shape filter syntax compile-time-resolved against
typed columns. Issue #269 / T1.7.
Each invocation lowers to the equivalent typed-column method call:
ⓘ
// These expand identically:
Q!(User.email__icontains = "alice")
User::email.ilike("%alice%")Field-name typos fail the build (the macro emits User::no_such_field
which doesn’t exist) — the headline ergonomic win of this slice over
Django’s stringly-typed __lookup filters.
§Supported lookup suffixes
- bare
=/__exact→.eq(value) __iexact→.ilike(value)(case-insensitive equality, no wildcards)__ne→.ne(value)__gt/__gte/__lt/__lte→ corresponding comparison__contains/__icontains→.like("%v%")/.ilike("%v%")__startswith/__istartswith→.like("v%")/.ilike("v%")__endswith/__iendswith→.like("%v")/.ilike("%v")__in→.is_in(iterable)__not_in→.not_in(iterable)__isnull = true→.is_null();__isnull = false→.is_not_null()__betweenaccepts a tuple literal(lo, hi)→.between(lo, hi)__regex/__iregex→.regex(pattern)/.iregex(pattern)
Unknown suffixes fail the build with a compile_error! pointing at
the lookup token.
§Combine
Each Q!() returns a TypedFilter<Model> — chain via the existing
.and() / .or() / .not() methods:
ⓘ
User::objects()
.where_(
Q!(User.active = true)
.and(Q!(User.email__icontains = "alice"))
)
.fetch(&pool).await?;All emitted code routes through existing per-dialect writers — no new SQL emission machinery. Tri-dialect support is inherent.