Pagination, Search, Ordering & Filtering¶
Pagination¶
BaseViewset.list (and AsyncBaseViewset.list) map limit and offset to query parameters on the LIST route.
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
int |
10 |
Maximum number of items to return (must be 0..10000, negative values are rejected with 422) |
offset |
int |
0 |
Number of items to skip (negative values are rejected with 422) |
No additional configuration needed — pagination is built into the default list() handler.
Search, ordering and filters via ListConfig¶
Since v1.5.0 the LIST endpoint supports server-side search, declarative
ordering and whitelisted filters. Declare an inner ListConfig class on
your Pydantic response schema:
from pydantic import BaseModel
class ItemSchema(BaseModel):
id: int
name: str
status: str
price: float
class ListConfig:
search_fields = ["name"] # ?search=foo → case-insensitive substring
ordering_fields = ["name", "price", "id"] # ?ordering= allowed fields
ordering = ["-id"] # default ordering (newest first)
filters = ["status", "price"] # ?<field>=... allowed fields
That is all — the default list() handler picks the configuration up
automatically.
Search¶
GET /items?search=foo performs a case-insensitive substring match,
OR-ed across every field in search_fields.
Ordering¶
GET /items?ordering=-price,name orders by price descending, then
name ascending. A leading - means descending.
- The field must be listed in
ordering_fields, otherwise the endpoint returns400 Bad Request. - When the
orderingparameter is omitted, the declarative default (ListConfig.ordering) applies. - When
ordering_fieldsis not declared, ordering via the query parameter is disabled (the parameter is ignored).
Filters¶
Any field listed in filters can be filtered with an exact-match query
parameter, and supports operator suffixes:
| Example | Meaning |
|---|---|
?status=active |
status == "active" |
?status__ne=active |
status != "active" |
?price__gt=100 |
price > 100 |
?price__gte=100 |
price >= 100 |
?price__lt=100 |
price < 100 |
?price__lte=100 |
price <= 100 |
?name__contains=pro |
case-insensitive substring |
?status__in=active,pending |
status IN (...) |
- Fields not listed in
filtersare ignored — arbitrary fields never reach the ORM. - Values are automatically coerced to
int/float/boolwhen they parse. - Everything composes:
?search=pro&status=active&ordering=-price&limit=5.
All built-in adapters (SQLAlchemy sync/async, Tortoise ORM, Peewee)
implement search, ordering and filters. The utility layer
(fastapi_viewsets.utils.get_list_queryset /
fastapi_viewsets.async_utils.get_list_queryset) also accepts
search, search_fields, ordering and filters keyword arguments
for programmatic use.
Backward compatibility¶
Without a ListConfig on the response schema, the LIST endpoint
behaves exactly as before: ?search= is ignored (no fields to search),
?ordering= is ignored, and unknown query parameters are dropped.
Custom filtering beyond the whitelist¶
For anything the declarative config does not cover, subclass
BaseViewset or AsyncBaseViewset and override list():
from typing import List, Optional
from fastapi import Depends
from sqlalchemy import select
from fastapi_viewsets import AsyncBaseViewset
class ItemsWithSearch(AsyncBaseViewset):
async def list(
self,
limit: int = 20,
offset: int = 0,
search: Optional[str] = None,
token: Optional[str] = Depends(lambda: None),
) -> list:
"""Custom LIST with search beyond the whitelist."""
session = self.db_session()
try:
stmt = select(self.model)
if search:
stmt = stmt.where(self.model.name.ilike(f"%{search}%"))
stmt = stmt.offset(offset).limit(limit)
rows = (await session.execute(stmt)).scalars().all()
return [self.response_model.model_validate(row) for row in rows]
finally:
await session.close()
See Overriding Handlers for the full example with search + ordering + conflict handling.
Roadmap¶
| Item | Target | Status |
|---|---|---|
Server-side search on LIST |
v1.5.0 | Released |
| Declarative ordering on LIST | v1.5.0 | Released |
Advanced filters (__gt, __lt, __in, ...) via query params |
v1.5.0 | Released |
Date-range filters (__range) and null checks (__isnull) |
future | Planned |
Transaction helpers (begin / atomic) across adapters |
future | Planned |
Next steps¶
- Overriding Handlers — complete override example
- API Reference —
list()signature and parameters