dynamicforms-fastapi-viewsets 0.3.5__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/.gitignore +3 -0
  2. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/PKG-INFO +47 -5
  3. dynamicforms_fastapi_viewsets-0.4.0/README.md +105 -0
  4. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/__init__.py +1 -0
  5. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/backends/__init__.py +7 -0
  6. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/backends/django_orm.py +479 -0
  7. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/collection_viewset.py +6 -1
  8. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/conf.py +14 -0
  9. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/cursor.py +293 -0
  10. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/decorators/__init__.py +8 -0
  11. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/decorators/build_schema.py +62 -7
  12. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/decorators/celery_viewset/__init__.py +7 -1
  13. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/decorators/celery_viewset/client.py +15 -8
  14. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/decorators/celery_viewset/server.py +1 -1
  15. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/decorators/route_viewset.py +120 -6
  16. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/endpoint_docs.py +147 -0
  17. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/filters/__init__.py +71 -0
  18. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/filters/base.py +93 -0
  19. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/filters/declaration.py +148 -0
  20. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/filters/operators.py +162 -0
  21. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/filters/registry.py +82 -0
  22. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/list_query.py +172 -0
  23. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/list_shapes.py +156 -0
  24. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/mixins.py +882 -0
  25. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/mux_ws/__init__.py +68 -0
  26. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/mux_ws/asgi.py +114 -0
  27. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/mux_ws/protocol.py +127 -0
  28. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/mux_ws/registry.py +174 -0
  29. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/mux_ws/server.py +117 -0
  30. dynamicforms_fastapi_viewsets-0.4.0/fastapi_viewsets/mux_ws/transports.py +40 -0
  31. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/pyproject.toml +10 -0
  32. dynamicforms_fastapi_viewsets-0.3.5/README.md +0 -66
  33. dynamicforms_fastapi_viewsets-0.3.5/fastapi_viewsets/__init__.py +0 -1
  34. dynamicforms_fastapi_viewsets-0.3.5/fastapi_viewsets/mixins.py +0 -489
  35. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/LICENSE +0 -0
  36. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/action_configuration.py +0 -0
  37. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/context/__init__.py +0 -0
  38. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/context/auth/__init__.py +0 -0
  39. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/context/auth/django.py +0 -0
  40. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/context/auth/jwt.py +0 -0
  41. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/context/auth/static.py +0 -0
  42. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/decorators/celery_viewset/result_reader.py +0 -0
  43. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/decorators/lifecycle_runner.py +0 -0
  44. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/decorators/primary_key_model_helper.py +0 -0
  45. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/middleware/__init__.py +0 -0
  46. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/middleware/auth/__init__.py +0 -0
  47. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/middleware/auth/authorization.py +0 -0
  48. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/middleware/rate_limiter.py +0 -0
  49. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/py.typed +0 -0
  50. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/response_classes.py +0 -0
  51. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/save_state/__init__.py +0 -0
  52. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/save_state/save_state.py +0 -0
  53. {dynamicforms_fastapi_viewsets-0.3.5 → dynamicforms_fastapi_viewsets-0.4.0}/fastapi_viewsets/save_state/serialize_state.py +0 -0
@@ -2,8 +2,11 @@
2
2
  node_modules
3
3
  coverage
4
4
  dist
5
+ /demo/test-results
6
+ /demo/backend/demo.sqlite3
5
7
 
6
8
  /celerybeat-schedule.db
9
+ /demo/backend/demo.sqlite3
7
10
 
8
11
  /docs/.vitepress/cache
9
12
  /package-lock.json
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: dynamicforms-fastapi-viewsets
3
- Version: 0.3.5
3
+ Version: 0.4.0
4
4
  Summary: Django REST Framework-style viewsets for FastAPI, with optional Celery-backed async execution and a Vue/TypeScript client counterpart.
5
5
  Project-URL: Homepage, https://github.com/dynamicforms/fastapi-viewsets
6
6
  Project-URL: Repository, https://github.com/dynamicforms/fastapi-viewsets
@@ -34,6 +34,7 @@ Requires-Dist: celery; extra == 'dev'
34
34
  Requires-Dist: coverage; extra == 'dev'
35
35
  Requires-Dist: django; extra == 'dev'
36
36
  Requires-Dist: httpx; extra == 'dev'
37
+ Requires-Dist: muxws[starlette,websockets]>=0.3.1; extra == 'dev'
37
38
  Requires-Dist: pyjwt; extra == 'dev'
38
39
  Requires-Dist: pytest; extra == 'dev'
39
40
  Requires-Dist: pytest-asyncio; extra == 'dev'
@@ -44,6 +45,8 @@ Requires-Dist: asgiref>=3.6; extra == 'django'
44
45
  Requires-Dist: django>=4.2; extra == 'django'
45
46
  Provides-Extra: jwt
46
47
  Requires-Dist: pyjwt>=2.8; extra == 'jwt'
48
+ Provides-Extra: muxws
49
+ Requires-Dist: muxws[starlette]>=0.3.1; extra == 'muxws'
47
50
  Description-Content-Type: text/markdown
48
51
 
49
52
  # DynamicForms FastAPI Viewsets
@@ -56,12 +59,29 @@ Celery-backed async execution and a matching Vue/TypeScript client counterpart.
56
59
  Handles type resolution, lifecycle management and OpenAPI schema automatically.
57
60
  - **`CollectionViewSet`** — zero-boilerplate in-memory viewset backed by any Python list, set or dict.
58
61
  Great for prototyping and testing.
62
+ - **`DjangoORMViewSet`** — back a viewset with a Django QuerySet. The filters it can compile, the
63
+ sort order with its NULL placement, the page as LIMIT/OFFSET and the total as `COUNT(*)` all
64
+ become SQL; a stage it declines falls back to the in-memory pass instead of failing, and
65
+ `pk_field_name` comes from the model's primary key (requires the `django` extra).
59
66
  - **`celery_viewset` decorator** — move a viewset's execution to a Celery worker with no code changes
60
67
  to the viewset itself, for long-running or background processing scenarios (requires the `celery`
61
68
  extra).
62
69
  - **Bulk operations** — first-class support for bulk create, update, partial update and destroy.
63
- - **Vue / TypeScript counterpart** — mirror mixin classes and a `route_rest` factory give you a fully
64
- typed HTTP client that matches your backend viewset exactly (published separately as
70
+ - **muxws transport** — reach the same viewsets over a single WebSocket instead of one HTTP request
71
+ per call. A command is dispatched into an app built from the endpoints published on muxws, each
72
+ carrying the route kwargs REST is given, so validation, dependencies, response models and the
73
+ command middleware from `settings.viewsets_command_middleware` behave identically; that app is the
74
+ library's own, so what you attached anywhere but the endpoint itself sees a command only if you
75
+ pass your app to `process_command`. In the demo, a burst of 100 requests takes 156 ms over REST
76
+ and 37 ms over muxws.
77
+ - **Three list shapes** — a bare array, offset paging, or cursor paging. A viewset declares which,
78
+ and may let a client pick per request with an `X-List-Shape` header.
79
+ - **Declarative filters** — declare which fields accept which operators and get query parameters,
80
+ an OpenAPI schema and filtering for free; backends translate what they can into their own query.
81
+ - **Vue / TypeScript counterpart** — mirror mixin classes and the `restViewSet` / `muxwsViewSet` class
82
+ factory give you a fully typed client that matches your backend viewset exactly: the mixins a ViewSet
83
+ declares are its public surface, so calling an action it did not declare is a compile error rather
84
+ than a runtime 404 (published separately as
65
85
  [`@dynamicforms/fastapi-viewsets`](https://www.npmjs.com/package/@dynamicforms/fastapi-viewsets) on npm).
66
86
 
67
87
  ## Installation
@@ -71,6 +91,12 @@ pip install dynamicforms-fastapi-viewsets
71
91
 
72
92
  # with Celery-backed viewset support
73
93
  pip install "dynamicforms-fastapi-viewsets[celery]"
94
+
95
+ # with the muxws WebSocket transport
96
+ pip install "dynamicforms-fastapi-viewsets[muxws]"
97
+
98
+ # with the Django ORM backend (Django 4.2+ and asgiref)
99
+ pip install "dynamicforms-fastapi-viewsets[django]"
74
100
  ```
75
101
 
76
102
  Requires Python 3.10+, FastAPI and Pydantic v2.
@@ -107,7 +133,23 @@ app.include_router(router)
107
133
  ```
108
134
 
109
135
  See the [full documentation](https://docs.velis.si/dynamicforms/fastapi-viewsets/) for guides on
110
- the mixin system, `route_viewset`, `CollectionViewSet`, `celery_viewset`, and the Vue client.
136
+ the mixin system, `route_viewset`, `CollectionViewSet`, `DjangoORMViewSet`, `celery_viewset`, the
137
+ list pipeline and pagination, the muxws transport, and the Vue client.
138
+
139
+ ## Demo
140
+
141
+ ```bash
142
+ python demo.py # backend on :8000, frontend on :5173
143
+ python demo.py --celery # ... with every viewset call routed through a Celery worker
144
+ npm run test:e2e # drives the demo in a browser
145
+ ```
146
+
147
+ An infinite-scrolling grid over a 5000-track library, cursor-paged. Switch the whole grid between
148
+ the REST and muxws transports and between the in-memory and SQLite backends, and compare their
149
+ latency side by side. Sorting and filtering are server-side.
150
+
151
+ `--celery` needs Redis on localhost:6379. The end-to-end suite starts its own backend and dev
152
+ server on their own ports.
111
153
 
112
154
  ## License
113
155
 
@@ -0,0 +1,105 @@
1
+ # DynamicForms FastAPI Viewsets
2
+
3
+ Django REST Framework-style viewsets for [FastAPI](https://fastapi.tiangolo.com/), with optional
4
+ Celery-backed async execution and a matching Vue/TypeScript client counterpart.
5
+
6
+ - **Python mixins for FastAPI** — compose CRUD and bulk endpoints from small, focused mixin classes.
7
+ - **`route_viewset` decorator** — register a viewset on a FastAPI router with a single decorator call.
8
+ Handles type resolution, lifecycle management and OpenAPI schema automatically.
9
+ - **`CollectionViewSet`** — zero-boilerplate in-memory viewset backed by any Python list, set or dict.
10
+ Great for prototyping and testing.
11
+ - **`DjangoORMViewSet`** — back a viewset with a Django QuerySet. The filters it can compile, the
12
+ sort order with its NULL placement, the page as LIMIT/OFFSET and the total as `COUNT(*)` all
13
+ become SQL; a stage it declines falls back to the in-memory pass instead of failing, and
14
+ `pk_field_name` comes from the model's primary key (requires the `django` extra).
15
+ - **`celery_viewset` decorator** — move a viewset's execution to a Celery worker with no code changes
16
+ to the viewset itself, for long-running or background processing scenarios (requires the `celery`
17
+ extra).
18
+ - **Bulk operations** — first-class support for bulk create, update, partial update and destroy.
19
+ - **muxws transport** — reach the same viewsets over a single WebSocket instead of one HTTP request
20
+ per call. A command is dispatched into an app built from the endpoints published on muxws, each
21
+ carrying the route kwargs REST is given, so validation, dependencies, response models and the
22
+ command middleware from `settings.viewsets_command_middleware` behave identically; that app is the
23
+ library's own, so what you attached anywhere but the endpoint itself sees a command only if you
24
+ pass your app to `process_command`. In the demo, a burst of 100 requests takes 156 ms over REST
25
+ and 37 ms over muxws.
26
+ - **Three list shapes** — a bare array, offset paging, or cursor paging. A viewset declares which,
27
+ and may let a client pick per request with an `X-List-Shape` header.
28
+ - **Declarative filters** — declare which fields accept which operators and get query parameters,
29
+ an OpenAPI schema and filtering for free; backends translate what they can into their own query.
30
+ - **Vue / TypeScript counterpart** — mirror mixin classes and the `restViewSet` / `muxwsViewSet` class
31
+ factory give you a fully typed client that matches your backend viewset exactly: the mixins a ViewSet
32
+ declares are its public surface, so calling an action it did not declare is a compile error rather
33
+ than a runtime 404 (published separately as
34
+ [`@dynamicforms/fastapi-viewsets`](https://www.npmjs.com/package/@dynamicforms/fastapi-viewsets) on npm).
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install dynamicforms-fastapi-viewsets
40
+
41
+ # with Celery-backed viewset support
42
+ pip install "dynamicforms-fastapi-viewsets[celery]"
43
+
44
+ # with the muxws WebSocket transport
45
+ pip install "dynamicforms-fastapi-viewsets[muxws]"
46
+
47
+ # with the Django ORM backend (Django 4.2+ and asgiref)
48
+ pip install "dynamicforms-fastapi-viewsets[django]"
49
+ ```
50
+
51
+ Requires Python 3.10+, FastAPI and Pydantic v2.
52
+
53
+ ## Quick example
54
+
55
+ ```python
56
+ from fastapi import APIRouter, FastAPI
57
+ from pydantic import BaseModel
58
+
59
+ from fastapi_viewsets.collection_viewset import CollectionViewSet
60
+ from fastapi_viewsets.decorators.route_viewset import route_viewset
61
+ from fastapi_viewsets.mixins import BulkViewSetMixin
62
+
63
+
64
+ class Item(BaseModel):
65
+ id: int
66
+ name: str
67
+
68
+
69
+ database: dict[int, Item] = {1: Item(id=1, name="First element")}
70
+
71
+ app = FastAPI()
72
+ router = APIRouter()
73
+
74
+
75
+ @route_viewset(router, base_path="/items", pk_field_name="id")
76
+ class ItemViewSet(CollectionViewSet[int, Item], BulkViewSetMixin[int, Item]):
77
+ def __init__(self):
78
+ super().__init__(container=database, pk_field="id")
79
+
80
+
81
+ app.include_router(router)
82
+ ```
83
+
84
+ See the [full documentation](https://docs.velis.si/dynamicforms/fastapi-viewsets/) for guides on
85
+ the mixin system, `route_viewset`, `CollectionViewSet`, `DjangoORMViewSet`, `celery_viewset`, the
86
+ list pipeline and pagination, the muxws transport, and the Vue client.
87
+
88
+ ## Demo
89
+
90
+ ```bash
91
+ python demo.py # backend on :8000, frontend on :5173
92
+ python demo.py --celery # ... with every viewset call routed through a Celery worker
93
+ npm run test:e2e # drives the demo in a browser
94
+ ```
95
+
96
+ An infinite-scrolling grid over a 5000-track library, cursor-paged. Switch the whole grid between
97
+ the REST and muxws transports and between the in-memory and SQLite backends, and compare their
98
+ latency side by side. Sorting and filtering are server-side.
99
+
100
+ `--celery` needs Redis on localhost:6379. The end-to-end suite starts its own backend and dev
101
+ server on their own ports.
102
+
103
+ ## License
104
+
105
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1 @@
1
+ __version__ = "0.4.0"
@@ -0,0 +1,7 @@
1
+ """
2
+ Concrete data-source backends for viewsets.
3
+
4
+ `CollectionViewSet` (in the package root) covers anything already in memory. Everything here backs
5
+ a viewset with a store that can answer part of a list query itself, and so has to translate the
6
+ pipeline's stages into that store's own language rather than filtering a list.
7
+ """