Proposal
Add an opt-in Table.filter(**kwargs) classmethod that accepts Django-style lookups and
returns a normal objects() query. It's pure sugar — .where() and the explicit style stay
the default.
# proposed
await Band.filter(popularity__gte=1000, manager__name="Guido")
# equivalent today
await Band.objects().where(
Band.popularity >= 1000,
Band.manager.name == "Guido",
)
Why
When filters arrive as data (API query params, config, dynamic sets), you can pass them
straight through — await Band.filter(**criteria) — instead of building expressions by hand.
For example, an endpoint like GET /bands?popularity__gte=1000&manager__name=Guido:
# request.query_params -> {"popularity__gte": "1000", "manager__name": "Guido"}
async def list_bands(request):
return await Band.filter(**request.query_params)
The lookup name in the URL is the lookup passed to the query — no translation layer.
Scope (first PR, deliberately small)
- bare field →
==
__in, __gte, __lte, __gt, __lt
- datetime transforms
__year / __month / __day / __hour (reusing your Year/etc.)
- nested FK paths:
manager__name__in
~40 lines: a lookup parser + the classmethod calling objects().where(...). No new query
machinery. Returns Objects, so it chains as usual.
Questions before I PR
- Open to this as opt-in (explicit style stays the default)?
- Prefer
Table.filter(**kwargs), or a standalone helper passed into .where(...)?
- Is the operator/transform set above right for a first PR, or trim further?
Happy to implement with tests + docs once you confirm the direction. Thanks!
Proposal
Add an opt-in
Table.filter(**kwargs)classmethod that accepts Django-style lookups andreturns a normal
objects()query. It's pure sugar —.where()and the explicit style staythe default.
Why
When filters arrive as data (API query params, config, dynamic sets), you can pass them
straight through —
await Band.filter(**criteria)— instead of building expressions by hand.For example, an endpoint like
GET /bands?popularity__gte=1000&manager__name=Guido:The lookup name in the URL is the lookup passed to the query — no translation layer.
Scope (first PR, deliberately small)
==__in,__gte,__lte,__gt,__lt__year/__month/__day/__hour(reusing yourYear/etc.)manager__name__in~40 lines: a lookup parser + the classmethod calling
objects().where(...). No new querymachinery. Returns
Objects, so it chains as usual.Questions before I PR
Table.filter(**kwargs), or a standalone helper passed into.where(...)?Happy to implement with tests + docs once you confirm the direction. Thanks!