QueryBuilder¶
QueryBuilder
¶
Bases: _Filterable, Generic[T]
Chainable query builder. Subclass to add project-specific methods.
Default chain shape::
Pet.query.eq("species", "cat").gte("age", 3).limit(10).all()
Subclass example — add a paginate(page, size) shortcut and bind it via
query_class= so every Model.query returns the custom builder::
class PaginatedQB(QueryBuilder):
def paginate(self, page: int, size: int = 50) -> "PaginatedQB":
return self.range(page * size, (page + 1) * size - 1)
class Pet(SupabaseModel, table="pets", query_class=PaginatedQB):
id: UUID
name: str
page_two = await Pet.query.eq("adopted", False).paginate(2).all()
Source code in src/supabase_orm/_async/_query.py
match
¶
Filter rows where every column == value pair in query holds.
PostgREST's match is multi-column by design — it has no single
column argument. Use it for compound equality filters::
await Pet.query.match({"species": "cat", "adopted": False}).all()
Equivalent to chaining .eq() per pair. Not available inside
or_() / not_() (no predicate-string form).
Source code in src/supabase_orm/_async/_query.py
order_by
¶
Order results by one or more columns.
Three accepted forms — mix freely::
Pet.query.order_by("-created_at", "name") # string shorthand
Pet.query.order_by(Pet.f.created_at.desc()) # typed
Pet.query.order_by(Pet.f.last_login.desc(nulls="last"))
Strings use the Django "-col" prefix for descending. The typed
form unlocks nulls="first" / "last" ordering, which strings
don't expose.
Source code in src/supabase_orm/_async/_query.py
as_
¶
Rebind the response shape.
Two modes:
- Same-table SupabaseModel — narrows the wire
selectto the target's__select__and validates against it. - Plain BaseModel — validation only, wire
selectunchanged. Use when filtering on source columns the lean target doesn't expose.
Cross-table SupabaseModel targets raise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
type[U]
|
A Pydantic |
required |
Source code in src/supabase_orm/_async/_query.py
values
async
¶
Run the query with an ad-hoc column projection. Returns raw dicts.
No Pydantic validation, no autocomplete — caller deals with
row["col"]. Use for exports or ad-hoc admin queries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*columns
|
str
|
One or more column names. May include PostgREST embed
syntax (e.g. |
()
|
Source code in src/supabase_orm/_async/_query.py
explain
¶
Resolved HTTP request — no execute.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
redact
|
bool
|
When True (default), auth headers ( |
True
|
Source code in src/supabase_orm/_async/_query.py
raw
¶
Return the underlying postgrest async builder for ops we don't model.
Resolves the client at call time, so the builder is bound to the
current ContextVar — pair with use_client() in a request
scope and the escape hatch sees the same client as the rest of
your handler. Pair with :func:supabase_orm.serialize if you
need wire-value coercion for non-JSON-native python types.
Source code in src/supabase_orm/_async/_query.py
all_with_count
async
¶
Run the query and ask PostgREST for an exact total in one round-trip.
Useful for paginated endpoints — saves a separate .count() call.
count="exact" is computed on the FILTERED row set, ignoring
limit/offset, which is the standard pagination semantics.
Source code in src/supabase_orm/_async/_query.py
count
async
¶
Count matching rows via a head-only request (no body, no validation).
Source code in src/supabase_orm/_async/_query.py
exists
async
¶
Return True iff at least one row matches the current filters.
Wire shape: select=<pk>&limit=1 — projects the PK only, fetches
at most one row, skips Pydantic validation. Cheap on tables of any
size, and unlike count() > 0 does not ask Postgres for an exact
total.
Source code in src/supabase_orm/_async/_query.py
iter
¶
Yield every matching row using PK keyset pagination.
Constant-time per batch (uses the PK index). Owns ordering and
pagination — chaining .order_by() / .limit() / .offset()
/ .range() before .iter() raises.
Snapshot semantics are loose: rows with pk > cursor inserted
mid-iteration are picked up; rows with pk < cursor are missed.
With monotonic PKs (UUIDv7, serial) the latter can't happen.
Race-safe for concurrent deletes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch_size
|
int
|
Rows per round-trip. |
1000
|
Source code in src/supabase_orm/_async/_query.py
delete
async
¶
delete(
*,
allow_unfiltered: bool = False,
returning: ReturnMode = "representation",
) -> list[T] | None
Bulk-delete every matching row.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
allow_unfiltered
|
bool
|
Required to delete every row when no filter is chained; PostgREST also rejects unfiltered DELETE server-side. |
False
|
returning
|
ReturnMode
|
|
'representation'
|
Returns:
| Type | Description |
|---|---|
list[T] | None
|
The deleted rows, or |
Source code in src/supabase_orm/_async/_query.py
update
async
¶
update(
*,
allow_unfiltered: bool = False,
returning: ReturnMode = "representation",
**values: Any,
) -> list[T] | None
Bulk-update every matching row.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
allow_unfiltered
|
bool
|
Required to update every row when no filter is chained. |
False
|
returning
|
ReturnMode
|
|
'representation'
|
**values
|
Any
|
Column=value pairs to set. |
{}
|
Returns:
| Type | Description |
|---|---|
list[T] | None
|
The updated rows, or |