fastapi-dynamic-filter
Dynamic, auto‑generating SQLAlchemy filters for FastAPI – with native support for JSONB operations, range queries, case‑insensitive search, and more.
Why this library?
When building a REST API with FastAPI and SQLAlchemy, you often need to let clients filter, search, sort, and apply range conditions on your models. Writing a separate Pydantic filter class for every endpoint is tedious and repetitive.
fastapi-dynamic-filter solves this by:
- Automatically generating filter fields from your SQLAlchemy model – just declare which columns you want to expose.
- Supporting PostgreSQL JSONB operators:
@>(contains),?(key presence), andILIKEon string‑casted JSON values. - Integrating seamlessly with fastapi-filter – you get all its power (sorting, pagination, etc.) without extra boilerplate.
- Generating OpenAPI (Swagger) documentation automatically – your API consumers see exactly what filters are available.
Installation
pip install fastapi-dynamic-filter
Quick Start
Assume you have a SQLAlchemy model User:
from sqlalchemy import Column, Integer, String, DateTime, Boolean
from sqlalchemy.dialects.postgresql import ARRAY, JSONB
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
email = Column(String, unique=True)
full_name = Column(String)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime)
tags = Column(ARRAY(String)) # PostgreSQL array
metadata = Column(JSONB) # PostgreSQL JSONB
Now create a filter class that inherits from DynamicFilter:
from fastapi_dynamic_filter import DynamicFilter
class UserFilter(DynamicFilter):
db_model = User
# Exact matches (equality)
exact_fields = ["id", "email", "is_active"]
# Range queries (__gte, __lte)
range_fields = ["created_at"]
# Case‑insensitive search (__ilike) – only for string columns
search_fields = ["full_name"]
# Array/JSONB contains & key presence
contains_fields = ["tags", "metadata"] # tags__contains, metadata__contains, metadata__has_key
# Case‑insensitive search inside JSONB values (casts to TEXT and ILIKE)
json_search_fields = ["metadata"] # metadata__value_ilike
# Default sorting (can be overridden by client)
default_order_by = ["-created_at"] # descending
That’s it! The filter class automatically gains all the corresponding Pydantic fields. Use it as a dependency in your FastAPI endpoint:
from fastapi import Depends, FastAPI
from sqlalchemy.orm import Session
app = FastAPI()
@app.get("/users")
def get_users(
filter: UserFilter = Depends(UserFilter),
db: Session = Depends(get_db),
):
query = db.query(User)
query = filter.filter(query) # apply all filters
query = filter.sort(query) # apply sorting (if any)
return query.all()
Your Swagger docs will now show a beautiful request body (or query parameters, depending on how you configure fastapi-filter) with all generated fields:
{
"id": 1,
"email": "john@example.com",
"is_active": true,
"created_at__gte": "2024-01-01T00:00:00Z",
"created_at__lte": "2024-12-31T23:59:59Z",
"full_name__ilike": "john",
"tags__contains": ["admin"],
"metadata__contains": {"role": "editor"},
"metadata__has_key": "role",
"metadata__value_ilike": "admin",
"order_by": ["-created_at"]
}
Field Reference
| Field list | Generated filter fields | Description |
|---|---|---|
exact_fields |
field (exact match) |
Equality comparison (==) |
search_fields |
field__ilike |
Case‑insensitive LIKE (only for string columns) |
range_fields |
field__gte, field__lte |
Greater‑than‑or‑equal / less‑than‑or‑equal (works with numeric, date, etc.) |
contains_fields |
field__contains (for arrays/dicts) |
PostgreSQL @> (array contains / JSONB contains) |
field__has_key (for dicts) |
PostgreSQL ? (JSONB key exists) |
|
json_search_fields |
field__value_ilike |
Casts JSONB to text and applies ILIKE (full‑text search inside JSON) |
default_order_by |
order_by (list of strings) |
Default sorting direction (prepend - for descending) |
All generated fields are optional – clients can send only the ones they need.
Why not just use fastapi-filter directly?
fastapi-filter is great, but it requires you to explicitly write every filter field and its type. For models with many columns, that's a lot of boilerplate. fastapi-dynamic-filter generates all those fields dynamically from your model, while still giving you full control over which columns are exposed.
Requirements
-
Python 3.10+
-
FastAPI 0.100+
-
SQLAlchemy 2.0+
-
fastapi-filter 2.0+
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastapi_dynamic_filter-0.1.1.tar.gz.
File metadata
- Download URL: fastapi_dynamic_filter-0.1.1.tar.gz
- Upload date:
- Size: 62.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
59ff1ccaf450321e2f9aee75f5e0e32125ab2e5d2a17e4cd289930e688698036
|
|
| MD5 |
f39aa72d45635ffe3038282b556004f2
|
|
| BLAKE2b-256 |
932e54438dab324c8ef46629ed9c97e9d6f6c3491a29f265ddd86582b032d039
|
File details
Details for the file fastapi_dynamic_filter-0.1.1-py3-none-any.whl.
File metadata
- Download URL: fastapi_dynamic_filter-0.1.1-py3-none-any.whl
- Upload date:
- Size: 6.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8fe375dbdf61198b51c633f091328d9e711d9aa938016e221876ef1283110fd9
|
|
| MD5 |
86f44a9b3b6db16b9f220d13837e2a3f
|
|
| BLAKE2b-256 |
f3291486fae85cc5bd1254aed4f4a221ceaa4688cf2813300532eadd092d23cf
|