DynamicForms FastAPI Viewsets
Django REST Framework-style viewsets for FastAPI, with optional Celery-backed async execution and a matching Vue/TypeScript client counterpart.
- Python mixins for FastAPI — compose CRUD and bulk endpoints from small, focused mixin classes.
route_viewsetdecorator — register a viewset on a FastAPI router with a single decorator call. Handles type resolution, lifecycle management and OpenAPI schema automatically.- Nested viewsets — mount a viewset under another one's path (
/accounts/members) withViewSetRouter.sub_viewset; a request to it passes the parent's checks and its own. CollectionViewSet— zero-boilerplate in-memory viewset backed by any Python list, set or dict. Great for prototyping and testing.DjangoORMViewSet— back a viewset with a Django QuerySet. The filters it can compile, the sort order with its NULL placement, the page as LIMIT/OFFSET and the total asCOUNT(*)all become SQL; a stage it declines falls back to the in-memory pass instead of failing, andpk_field_namecomes from the model's primary key (requires thedjangoextra).celery_viewsetdecorator — move a viewset's execution to a Celery worker with no code changes to the viewset itself, for long-running or background processing scenarios (requires theceleryextra).- Bulk operations — first-class support for bulk create, update, partial update and destroy.
- muxws transport — reach the same viewsets over a single WebSocket instead of one HTTP request per call. A command goes through your application as an HTTP request does - its middleware, exception handlers, router dependencies and state - so both transports behave identically.
- Events — publish an event to a topic and every frontend following it learns of it, as a muxws
push or on its next poll.
ChangeEventspublishes every create, update and destroy of a viewset to its routed base path; custom events are declared withsource(). Every event is recorded before it is pushed, so a dropped push costs latency, not an event. On the frontend,watch()on a ViewSet follows its topic andapplyChanges()applies each change to a list of rows. - Three list shapes — a bare array, offset paging, or cursor paging. A viewset declares which,
and may let a client pick per request with an
X-List-Shapeheader. - Declarative filters — declare which fields accept which operators and get query parameters, an OpenAPI schema and filtering for free; backends translate what they can into their own query.
- Vue / TypeScript counterpart — mirror mixin classes and the
restViewSet/muxwsViewSetclass factory give you a fully typed client that matches your backend viewset exactly: the mixins a ViewSet declares are its public surface, so calling an action it did not declare is a compile error rather than a runtime 404 (published separately as@dynamicforms/fastapi-viewsetson npm).
Installation
pip install dynamicforms-fastapi-viewsets
# with Celery-backed viewset support
pip install "dynamicforms-fastapi-viewsets[celery]"
# with the muxws WebSocket transport
pip install "dynamicforms-fastapi-viewsets[muxws]"
# with the Django ORM backend (Django 4.2+ and asgiref)
pip install "dynamicforms-fastapi-viewsets[django]"
Requires Python 3.10+, FastAPI and Pydantic 2.11+.
Quick example
from fastapi import APIRouter, FastAPI
from pydantic import BaseModel
from fastapi_viewsets.collection_viewset import CollectionViewSet
from fastapi_viewsets.decorators.route_viewset import route_viewset
from fastapi_viewsets.mixins import BulkViewSetMixin
class Item(BaseModel):
id: int
name: str
database: dict[int, Item] = {1: Item(id=1, name="First element")}
app = FastAPI()
router = APIRouter()
@route_viewset(router, base_path="/items", pk_field_name="id")
class ItemViewSet(CollectionViewSet[int, Item], BulkViewSetMixin[int, Item]):
def __init__(self):
super().__init__(container=database, pk_field="id")
app.include_router(router)
See the full documentation for guides on
the mixin system, route_viewset, CollectionViewSet, DjangoORMViewSet, celery_viewset, the
list pipeline and pagination, the muxws transport, events, and the Vue client.
Demo
python demo.py # backend on :8000, frontend on :5173
python demo.py --celery # ... with every viewset call routed through a Celery worker
npm run test:e2e # drives the demo in a browser
An infinite-scrolling grid over a 5000-track library, cursor-paged. Switch the whole grid between the REST and muxws transports and between the in-memory and SQLite backends, and compare their latency side by side. Sorting and filtering are server-side.
The grid follows its viewset's change events (ChangeEvents on the backend, watch() in the
grid). Click a Favorite cell to toggle it: every tab showing the library applies the change, over
muxws as a push, over REST on its next poll. A sorted or filtered grid reloads its list on the
change: the row may have moved or left it.
--celery needs Redis on localhost:6379. The worker runs the viewset calls of both backends and
publishes their events through Redis. It runs as one process, because the in-memory library is a
dict held by each process; the SQLite library is one file shared by every process. The end-to-end
suite starts its own backend and dev server on their own ports.
License
MIT — see LICENSE.
Metadata
Release files for dynamicforms-fastapi-viewsets 0.14.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| dynamicforms_fastapi_viewsets-0.14.2.tar.gz | 130.8 kB | Without provenance | Details for dynamicforms_fastapi_viewsets-0.14.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| dynamicforms_fastapi_viewsets-0.14.2-py3-none-any.whl | Python 3 | none | any | Details for dynamicforms_fastapi_viewsets-0.14.2-py3-none-any.whl |
Total release size: 291.3 kB
Release files / dynamicforms_fastapi_viewsets-0.14.2.tar.gz
| Download URL | dynamicforms_fastapi_viewsets-0.14.2.tar.gz |
|---|---|
| Size | 130.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7fd37690b6eeaa1b5bfdf2f91da2350b2558b9c211aed0562fd287205f1359c9
|
|
BLAKE2b-256 checksum How to use checksums |
f6b71777c41b1148887e64261685a530bb00aee532ce6cd45011a1b9dc835107
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / dynamicforms_fastapi_viewsets-0.14.2-py3-none-any.whl
| Download URL | dynamicforms_fastapi_viewsets-0.14.2-py3-none-any.whl |
|---|---|
| Size | 160.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0bcfdbf27c3ade7235237c4ee04ac224eb6ab1b35bbd4a138e782ebe82b47330
|
|
BLAKE2b-256 checksum How to use checksums |
c158ff2934bb3cd34c66a374b1d2b4df919b9457d0a46e68e213ba52f73e0140
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for dynamicforms-fastapi-viewsets 0.14.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.