Skip to main content

Q

Macro Q 

Source
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()
  • __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(&pool).await?;

All emitted code routes through existing per-dialect writers — no new SQL emission machinery. Tri-dialect support is inherent.