crud¶
Here's the reference for the CRUD classes, factory, and search utilities.
You can import the main symbols from fastapi_toolsets.crud:
from fastapi_toolsets.crud import CrudFactory, AsyncCrud
from fastapi_toolsets.crud.search import SearchConfig, get_searchable_fields, build_search_filters
fastapi_toolsets.crud.factory.AsyncCrud
¶
Bases: Generic[ModelType]
Generic async CRUD operations for SQLAlchemy models.
Subclass this and set the model class variable, or use CrudFactory.
count(session, filters=None, *, joins=None, outer_join=False)
async
classmethod
¶
Count records matching the filters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session |
required |
filters
|
list[Any] | None
|
List of SQLAlchemy filter conditions |
None
|
joins
|
JoinType | None
|
List of (model, condition) tuples for joining related tables |
None
|
outer_join
|
bool
|
Use LEFT OUTER JOIN instead of INNER JOIN |
False
|
Returns:
| Type | Description |
|---|---|
int
|
Number of matching records |
create(session, obj, *, schema=None)
async
classmethod
¶
Create a new record in the database.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session |
required |
obj
|
BaseModel
|
Pydantic model with data to create |
required |
schema
|
type[BaseModel] | None
|
Pydantic schema to serialize the result into. When provided,
the result is automatically wrapped in a |
None
|
Returns:
| Type | Description |
|---|---|
ModelType | Response[Any]
|
Created model instance, or |
cursor_paginate(session, *, cursor=None, filters=None, joins=None, outer_join=False, load_options=None, order_by=None, order_joins=None, items_per_page=20, search=None, search_fields=None, search_column=None, order_fields=None, facet_fields=None, filter_by=None, schema)
async
classmethod
¶
Get paginated results using cursor-based pagination.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session. |
required |
cursor
|
str | None
|
Cursor string from a previous |
None
|
filters
|
list[Any] | None
|
List of SQLAlchemy filter conditions. |
None
|
joins
|
JoinType | None
|
List of |
None
|
outer_join
|
bool
|
Use LEFT OUTER JOIN instead of INNER JOIN. |
False
|
load_options
|
Sequence[ExecutableOption] | None
|
SQLAlchemy loader options. Falls back to
|
None
|
order_by
|
OrderByClause | None
|
Additional ordering applied after the cursor column. |
None
|
items_per_page
|
int
|
Number of items per page (default 20). |
20
|
search
|
str | SearchConfig | None
|
Search query string or SearchConfig object. |
None
|
search_fields
|
Sequence[SearchFieldType] | None
|
Fields to search in (overrides class default). |
None
|
search_column
|
str | None
|
Restrict search to a single column key. |
None
|
order_fields
|
Sequence[OrderFieldType] | None
|
Fields allowed for sorting (overrides class default). |
None
|
facet_fields
|
Sequence[FacetFieldType] | None
|
Columns to compute distinct values for (overrides class default). |
None
|
filter_by
|
dict[str, Any] | BaseModel | None
|
Dict of {column_key: value} to filter by declared facet fields. Keys must match the column.key of a facet field. Scalar → equality, list → IN clause. Raises InvalidFacetFilterError for unknown keys. |
None
|
schema
|
type[BaseModel]
|
Optional Pydantic schema to serialize each item into. |
required |
Returns:
| Type | Description |
|---|---|
CursorPaginatedResponse[Any]
|
PaginatedResponse with CursorPagination metadata |
cursor_paginate_params(*, default_page_size=20, max_page_size=100, search=True, filter=True, order=True, search_fields=None, facet_fields=None, order_fields=None, default_order_field=None, default_order='asc')
classmethod
¶
Return a FastAPI dependency that collects all params for :meth:cursor_paginate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default_page_size
|
int
|
Default |
20
|
max_page_size
|
int
|
Maximum |
100
|
search
|
bool
|
Enable search query parameters. |
True
|
filter
|
bool
|
Enable facet filter query parameters. |
True
|
order
|
bool
|
Enable order query parameters. |
True
|
search_fields
|
Sequence[SearchFieldType] | None
|
Override searchable fields. |
None
|
facet_fields
|
Sequence[FacetFieldType] | None
|
Override facet fields. |
None
|
order_fields
|
Sequence[OrderFieldType] | None
|
Override order fields. |
None
|
default_order_field
|
QueryableAttribute[Any] | None
|
Default field to order by when |
None
|
default_order
|
Literal['asc', 'desc']
|
Default sort direction. |
'asc'
|
Returns:
| Name | Type | Description |
|---|---|---|
Callable[..., Awaitable[dict[str, Any]]]
|
An async dependency that resolves to a dict ready to be unpacked |
|
into |
Callable[..., Awaitable[dict[str, Any]]]
|
meth: |
delete(session, filters, *, return_response=False)
async
classmethod
¶
Delete records from the database.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session |
required |
filters
|
list[Any]
|
List of SQLAlchemy filter conditions |
required |
return_response
|
bool
|
When |
False
|
Returns:
| Type | Description |
|---|---|
None | Response[None]
|
|
exists(session, filters, *, joins=None, outer_join=False)
async
classmethod
¶
Check if a record exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session |
required |
filters
|
list[Any]
|
List of SQLAlchemy filter conditions |
required |
joins
|
JoinType | None
|
List of (model, condition) tuples for joining related tables |
None
|
outer_join
|
bool
|
Use LEFT OUTER JOIN instead of INNER JOIN |
False
|
Returns:
| Type | Description |
|---|---|
bool
|
True if at least one record matches |
first(session, filters=None, *, joins=None, outer_join=False, with_for_update=False, load_options=None, schema=None)
async
classmethod
¶
Get the first matching record, or None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session |
required |
filters
|
list[Any] | None
|
List of SQLAlchemy filter conditions |
None
|
joins
|
JoinType | None
|
List of (model, condition) tuples for joining related tables |
None
|
outer_join
|
bool
|
Use LEFT OUTER JOIN instead of INNER JOIN |
False
|
with_for_update
|
bool
|
Lock the row for update |
False
|
load_options
|
Sequence[ExecutableOption] | None
|
SQLAlchemy loader options (e.g., selectinload) |
None
|
schema
|
type[BaseModel] | None
|
Pydantic schema to serialize the result into. When provided,
the result is automatically wrapped in a |
None
|
Returns:
| Type | Description |
|---|---|
ModelType | Response[Any] | None
|
Model instance, |
ModelType | Response[Any] | None
|
or |
get(session, filters, *, joins=None, outer_join=False, with_for_update=False, load_options=None, schema=None)
async
classmethod
¶
Get exactly one record. Raises NotFoundError if not found.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session |
required |
filters
|
list[Any]
|
List of SQLAlchemy filter conditions |
required |
joins
|
JoinType | None
|
List of (model, condition) tuples for joining related tables |
None
|
outer_join
|
bool
|
Use LEFT OUTER JOIN instead of INNER JOIN |
False
|
with_for_update
|
bool
|
Lock the row for update |
False
|
load_options
|
Sequence[ExecutableOption] | None
|
SQLAlchemy loader options (e.g., selectinload) |
None
|
schema
|
type[BaseModel] | None
|
Pydantic schema to serialize the result into. When provided,
the result is automatically wrapped in a |
None
|
Returns:
| Type | Description |
|---|---|
ModelType | Response[Any]
|
Model instance, or |
Raises:
| Type | Description |
|---|---|
NotFoundError
|
If no record found |
MultipleResultsFound
|
If more than one record found |
get_multi(session, *, filters=None, joins=None, outer_join=False, load_options=None, order_by=None, limit=None, offset=None)
async
classmethod
¶
Get multiple records from the database.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session |
required |
filters
|
list[Any] | None
|
List of SQLAlchemy filter conditions |
None
|
joins
|
JoinType | None
|
List of (model, condition) tuples for joining related tables |
None
|
outer_join
|
bool
|
Use LEFT OUTER JOIN instead of INNER JOIN |
False
|
load_options
|
Sequence[ExecutableOption] | None
|
SQLAlchemy loader options |
None
|
order_by
|
OrderByClause | None
|
Column or list of columns to order by |
None
|
limit
|
int | None
|
Max number of rows to return |
None
|
offset
|
int | None
|
Rows to skip |
None
|
Returns:
| Type | Description |
|---|---|
Sequence[ModelType]
|
List of model instances |
get_or_none(session, filters, *, joins=None, outer_join=False, with_for_update=False, load_options=None, schema=None)
async
classmethod
¶
Get exactly one record, or None if not found.
Like :meth:get but returns None instead of raising
:class:~fastapi_toolsets.exceptions.NotFoundError when no record
matches the filters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session |
required |
filters
|
list[Any]
|
List of SQLAlchemy filter conditions |
required |
joins
|
JoinType | None
|
List of (model, condition) tuples for joining related tables |
None
|
outer_join
|
bool
|
Use LEFT OUTER JOIN instead of INNER JOIN |
False
|
with_for_update
|
bool
|
Lock the row for update |
False
|
load_options
|
Sequence[ExecutableOption] | None
|
SQLAlchemy loader options (e.g., selectinload) |
None
|
schema
|
type[BaseModel] | None
|
Pydantic schema to serialize the result into. When provided,
the result is automatically wrapped in a |
None
|
Returns:
| Type | Description |
|---|---|
ModelType | Response[Any] | None
|
Model instance, |
ModelType | Response[Any] | None
|
or |
Raises:
| Type | Description |
|---|---|
MultipleResultsFound
|
If more than one record found |
offset_paginate(session, *, filters=None, joins=None, outer_join=False, load_options=None, order_by=None, order_joins=None, page=1, items_per_page=20, include_total=True, search=None, search_fields=None, search_column=None, order_fields=None, facet_fields=None, filter_by=None, schema)
async
classmethod
¶
Get paginated results using offset-based pagination.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session |
required |
filters
|
list[Any] | None
|
List of SQLAlchemy filter conditions |
None
|
joins
|
JoinType | None
|
List of (model, condition) tuples for joining related tables |
None
|
outer_join
|
bool
|
Use LEFT OUTER JOIN instead of INNER JOIN |
False
|
load_options
|
Sequence[ExecutableOption] | None
|
SQLAlchemy loader options |
None
|
order_by
|
OrderByClause | None
|
Column or list of columns to order by |
None
|
page
|
int
|
Page number (1-indexed) |
1
|
items_per_page
|
int
|
Number of items per page |
20
|
include_total
|
bool
|
When |
True
|
search
|
str | SearchConfig | None
|
Search query string or SearchConfig object |
None
|
search_fields
|
Sequence[SearchFieldType] | None
|
Fields to search in (overrides class default) |
None
|
search_column
|
str | None
|
Restrict search to a single column key. |
None
|
order_fields
|
Sequence[OrderFieldType] | None
|
Fields allowed for sorting (overrides class default). |
None
|
facet_fields
|
Sequence[FacetFieldType] | None
|
Columns to compute distinct values for (overrides class default) |
None
|
filter_by
|
dict[str, Any] | BaseModel | None
|
Dict of {column_key: value} to filter by declared facet fields. Keys must match the column.key of a facet field. Scalar → equality, list → IN clause. Raises InvalidFacetFilterError for unknown keys. |
None
|
schema
|
type[BaseModel]
|
Pydantic schema to serialize each item into. |
required |
Returns:
| Type | Description |
|---|---|
OffsetPaginatedResponse[Any]
|
PaginatedResponse with OffsetPagination metadata |
offset_paginate_params(*, default_page_size=20, max_page_size=100, include_total=True, search=True, filter=True, order=True, search_fields=None, facet_fields=None, order_fields=None, default_order_field=None, default_order='asc')
classmethod
¶
Return a FastAPI dependency that collects all params for :meth:offset_paginate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default_page_size
|
int
|
Default |
20
|
max_page_size
|
int
|
Maximum |
100
|
include_total
|
bool
|
Whether to include total count (not a query param). |
True
|
search
|
bool
|
Enable search query parameters. |
True
|
filter
|
bool
|
Enable facet filter query parameters. |
True
|
order
|
bool
|
Enable order query parameters. |
True
|
search_fields
|
Sequence[SearchFieldType] | None
|
Override searchable fields. |
None
|
facet_fields
|
Sequence[FacetFieldType] | None
|
Override facet fields. |
None
|
order_fields
|
Sequence[OrderFieldType] | None
|
Override order fields. |
None
|
default_order_field
|
QueryableAttribute[Any] | None
|
Default field to order by when |
None
|
default_order
|
Literal['asc', 'desc']
|
Default sort direction. |
'asc'
|
Returns:
| Name | Type | Description |
|---|---|---|
Callable[..., Awaitable[dict[str, Any]]]
|
An async dependency that resolves to a dict ready to be unpacked |
|
into |
Callable[..., Awaitable[dict[str, Any]]]
|
meth: |
paginate(session, *, pagination_type=PaginationType.OFFSET, filters=None, joins=None, outer_join=False, load_options=None, order_by=None, order_joins=None, page=1, cursor=None, items_per_page=20, include_total=True, search=None, search_fields=None, search_column=None, order_fields=None, facet_fields=None, filter_by=None, schema)
async
classmethod
¶
paginate(
session: AsyncSession,
*,
pagination_type: Literal[PaginationType.OFFSET],
filters: list[Any] | None = ...,
joins: JoinType | None = ...,
outer_join: bool = ...,
load_options: Sequence[ExecutableOption] | None = ...,
order_by: OrderByClause | None = ...,
order_joins: list[Any] | None = ...,
page: int = ...,
cursor: str | None = ...,
items_per_page: int = ...,
include_total: bool = ...,
search: str | SearchConfig | None = ...,
search_fields: Sequence[SearchFieldType] | None = ...,
search_column: str | None = ...,
order_fields: Sequence[OrderFieldType] | None = ...,
facet_fields: Sequence[FacetFieldType] | None = ...,
filter_by: dict[str, Any] | BaseModel | None = ...,
schema: type[BaseModel],
) -> OffsetPaginatedResponse[Any]
paginate(
session: AsyncSession,
*,
pagination_type: Literal[PaginationType.CURSOR],
filters: list[Any] | None = ...,
joins: JoinType | None = ...,
outer_join: bool = ...,
load_options: Sequence[ExecutableOption] | None = ...,
order_by: OrderByClause | None = ...,
order_joins: list[Any] | None = ...,
page: int = ...,
cursor: str | None = ...,
items_per_page: int = ...,
include_total: bool = ...,
search: str | SearchConfig | None = ...,
search_fields: Sequence[SearchFieldType] | None = ...,
search_column: str | None = ...,
order_fields: Sequence[OrderFieldType] | None = ...,
facet_fields: Sequence[FacetFieldType] | None = ...,
filter_by: dict[str, Any] | BaseModel | None = ...,
schema: type[BaseModel],
) -> CursorPaginatedResponse[Any]
Get paginated results using either offset or cursor pagination.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session. |
required |
pagination_type
|
PaginationType
|
Pagination strategy. Defaults to
|
OFFSET
|
filters
|
list[Any] | None
|
List of SQLAlchemy filter conditions. |
None
|
joins
|
JoinType | None
|
List of |
None
|
outer_join
|
bool
|
Use LEFT OUTER JOIN instead of INNER JOIN. |
False
|
load_options
|
Sequence[ExecutableOption] | None
|
SQLAlchemy loader options. Falls back to
|
None
|
order_by
|
OrderByClause | None
|
Column or expression to order results by. |
None
|
page
|
int
|
Page number (1-indexed). Only used when
|
1
|
cursor
|
str | None
|
Cursor token from a previous
:class: |
None
|
items_per_page
|
int
|
Number of items per page (default 20). |
20
|
include_total
|
bool
|
When |
True
|
search
|
str | SearchConfig | None
|
Search query string or :class: |
None
|
search_fields
|
Sequence[SearchFieldType] | None
|
Fields to search in (overrides class default). |
None
|
search_column
|
str | None
|
Restrict search to a single column key. |
None
|
order_fields
|
Sequence[OrderFieldType] | None
|
Fields allowed for sorting (overrides class default). |
None
|
facet_fields
|
Sequence[FacetFieldType] | None
|
Columns to compute distinct values for (overrides class default). |
None
|
filter_by
|
dict[str, Any] | BaseModel | None
|
Dict of |
None
|
schema
|
type[BaseModel]
|
Pydantic schema to serialize each item into. |
required |
Returns:
| Type | Description |
|---|---|
OffsetPaginatedResponse[Any] | CursorPaginatedResponse[Any]
|
class: |
OffsetPaginatedResponse[Any] | CursorPaginatedResponse[Any]
|
|
OffsetPaginatedResponse[Any] | CursorPaginatedResponse[Any]
|
|
paginate_params(*, default_page_size=20, max_page_size=100, default_pagination_type=PaginationType.OFFSET, include_total=True, search=True, filter=True, order=True, search_fields=None, facet_fields=None, order_fields=None, default_order_field=None, default_order='asc')
classmethod
¶
Return a FastAPI dependency that collects all params for :meth:paginate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default_page_size
|
int
|
Default |
20
|
max_page_size
|
int
|
Maximum |
100
|
default_pagination_type
|
PaginationType
|
Default pagination strategy. |
OFFSET
|
include_total
|
bool
|
Whether to include total count (not a query param). |
True
|
search
|
bool
|
Enable search query parameters. |
True
|
filter
|
bool
|
Enable facet filter query parameters. |
True
|
order
|
bool
|
Enable order query parameters. |
True
|
search_fields
|
Sequence[SearchFieldType] | None
|
Override searchable fields. |
None
|
facet_fields
|
Sequence[FacetFieldType] | None
|
Override facet fields. |
None
|
order_fields
|
Sequence[OrderFieldType] | None
|
Override order fields. |
None
|
default_order_field
|
QueryableAttribute[Any] | None
|
Default field to order by when |
None
|
default_order
|
Literal['asc', 'desc']
|
Default sort direction. |
'asc'
|
Returns:
| Name | Type | Description |
|---|---|---|
Callable[..., Awaitable[dict[str, Any]]]
|
An async dependency that resolves to a dict ready to be unpacked |
|
into |
Callable[..., Awaitable[dict[str, Any]]]
|
meth: |
update(session, obj, filters, *, exclude_unset=True, exclude_none=False, schema=None)
async
classmethod
¶
Update a record in the database.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session |
required |
obj
|
BaseModel
|
Pydantic model with update data |
required |
filters
|
list[Any]
|
List of SQLAlchemy filter conditions |
required |
exclude_unset
|
bool
|
Exclude fields not explicitly set in the schema |
True
|
exclude_none
|
bool
|
Exclude fields with None value |
False
|
schema
|
type[BaseModel] | None
|
Pydantic schema to serialize the result into. When provided,
the result is automatically wrapped in a |
None
|
Returns:
| Type | Description |
|---|---|
ModelType | Response[Any]
|
Updated model instance, or |
Raises:
| Type | Description |
|---|---|
NotFoundError
|
If no record found |
upsert(session, obj, index_elements, *, set_=None, where=None)
async
classmethod
¶
Create or update a record (PostgreSQL only).
Uses INSERT ... ON CONFLICT for atomic upsert.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
DB async session |
required |
obj
|
BaseModel
|
Pydantic model with data |
required |
index_elements
|
list[str]
|
Columns for ON CONFLICT (unique constraint) |
required |
set_
|
BaseModel | None
|
Pydantic model for ON CONFLICT DO UPDATE SET |
None
|
where
|
WhereHavingRole | None
|
WHERE clause for ON CONFLICT DO UPDATE |
None
|
Returns:
| Type | Description |
|---|---|
ModelType | None
|
Model instance |
fastapi_toolsets.crud.factory.CrudFactory(model, *, base_class=AsyncCrud, searchable_fields=None, facet_fields=None, order_fields=None, m2m_fields=None, default_load_options=None, cursor_column=None)
¶
Create a CRUD class for a specific model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[ModelType]
|
SQLAlchemy model class |
required |
base_class
|
type[AsyncCrud[Any]]
|
Optional base class to inherit from instead of |
AsyncCrud
|
searchable_fields
|
Sequence[SearchFieldType] | None
|
Optional list of searchable fields |
None
|
facet_fields
|
Sequence[FacetFieldType] | None
|
Optional list of columns to compute distinct values for in paginated
responses. Supports direct columns ( |
None
|
order_fields
|
Sequence[OrderFieldType] | None
|
Optional list of model attributes that callers are allowed to order by
via |
None
|
m2m_fields
|
M2MFieldType | None
|
Optional mapping for many-to-many relationships. Maps schema field names (containing lists of IDs) to SQLAlchemy relationship attributes. |
None
|
default_load_options
|
Sequence[ExecutableOption] | None
|
Default SQLAlchemy loader options applied to all read
queries when no explicit |
None
|
cursor_column
|
Any | None
|
Required to call |
None
|
Returns:
| Type | Description |
|---|---|
type[AsyncCrud[ModelType]]
|
AsyncCrud subclass bound to the model |
Example
from fastapi_toolsets.crud import CrudFactory
from myapp.models import User, Post
UserCrud = CrudFactory(User)
PostCrud = CrudFactory(Post)
# With searchable fields:
UserCrud = CrudFactory(
User,
searchable_fields=[User.username, User.email, (User.role, Role.name)]
)
# With many-to-many fields:
# Schema has `tag_ids: list[UUID]`, model has `tags` relationship to Tag
PostCrud = CrudFactory(
Post,
m2m_fields={"tag_ids": Post.tags},
)
# With facet fields for filter dropdowns / faceted search:
UserCrud = CrudFactory(
User,
facet_fields=[User.status, User.country, (User.role, Role.name)],
)
# With a fixed cursor column for cursor_paginate:
PostCrud = CrudFactory(
Post,
cursor_column=Post.created_at,
)
# With default load strategy (replaces lazy="selectin" on the model):
ArticleCrud = CrudFactory(
Article,
default_load_options=[selectinload(Article.category), selectinload(Article.tags)],
)
# Override default_load_options for a specific call:
article = await ArticleCrud.get(
session,
[Article.id == 1],
load_options=[selectinload(Article.category)], # tags won't load
)
# Usage
user = await UserCrud.get(session, [User.id == 1])
posts = await PostCrud.get_multi(session, filters=[Post.user_id == user.id])
# Create with M2M - tag_ids are automatically resolved
post = await PostCrud.create(session, PostCreate(title="Hello", tag_ids=[id1, id2]))
# With search
result = await UserCrud.offset_paginate(session, search="john")
# With joins (inner join by default):
users = await UserCrud.get_multi(
session,
joins=[(Post, Post.user_id == User.id)],
filters=[Post.published == True],
)
# With outer join:
users = await UserCrud.get_multi(
session,
joins=[(Post, Post.user_id == User.id)],
outer_join=True,
)
# With a shared custom base class:
from typing import Generic, TypeVar
from sqlalchemy.orm import DeclarativeBase
T = TypeVar("T", bound=DeclarativeBase)
class AuditedCrud(AsyncCrud[T], Generic[T]):
@classmethod
async def get_active(cls, session):
return await cls.get_multi(session, filters=[cls.model.is_active == True])
UserCrud = CrudFactory(User, base_class=AuditedCrud)
fastapi_toolsets.crud.search.SearchConfig
dataclass
¶
Advanced search configuration.
Attributes:
| Name | Type | Description |
|---|---|---|
query |
str
|
The search string |
fields |
Sequence[SearchFieldType] | None
|
Fields to search (columns or tuples for relationships) |
case_sensitive |
bool
|
Case-sensitive search (default: False) |
match_mode |
Literal['any', 'all']
|
"any" (OR) or "all" (AND) to combine fields |
fastapi_toolsets.crud.search.get_searchable_fields(model, *, include_relationships=True, max_depth=1)
cached
¶
Auto-detect String fields on a model and its relationships.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[DeclarativeBase]
|
SQLAlchemy model class |
required |
include_relationships
|
bool
|
Include fields from many-to-one/one-to-one relationships |
True
|
max_depth
|
int
|
Max depth for relationship traversal (default: 1) |
1
|
Returns:
| Type | Description |
|---|---|
list[SearchFieldType]
|
List of columns and tuples (relationship, column) |
fastapi_toolsets.crud.search.build_search_filters(model, search, search_fields=None, default_fields=None, search_column=None)
¶
Build SQLAlchemy filter conditions for search.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[DeclarativeBase]
|
SQLAlchemy model class |
required |
search
|
str | SearchConfig
|
Search string or SearchConfig |
required |
search_fields
|
Sequence[SearchFieldType] | None
|
Fields specified per-call (takes priority) |
None
|
default_fields
|
Sequence[SearchFieldType] | None
|
Default fields (from ClassVar) |
None
|
search_column
|
str | None
|
Optional key to narrow search to a single field. Must match one of the resolved search field keys. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[list[ColumnElement[bool]], list[InstrumentedAttribute[Any]]]
|
Tuple of (filter_conditions, joins_needed) |
Raises:
| Type | Description |
|---|---|
NoSearchableFieldsError
|
If no searchable field has been configured |