Skip to main content

Q

Macro Q 

Source
Q!() { /* proc-macro */ }
Expand description

Q!() — Model.field__lookup = value filter syntax, resolved at compile time 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 filters keyed by a plain string.

§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()
  • __between accepts 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.