fastapi-viewsets 1.5.0__tar.gz → 1.5.2__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.
- fastapi_viewsets-1.5.2/MANIFEST.in +3 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/PKG-INFO +68 -14
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/README.md +64 -10
- fastapi_viewsets-1.5.2/docs/api-reference.md +311 -0
- fastapi_viewsets-1.5.2/docs/assets/custom.css +25 -0
- fastapi_viewsets-1.5.2/docs/authentication.md +129 -0
- fastapi_viewsets-1.5.2/docs/changelog.md +37 -0
- fastapi_viewsets-1.5.2/docs/comparison.md +67 -0
- fastapi_viewsets-1.5.2/docs/custom-routes.md +114 -0
- fastapi_viewsets-1.5.2/docs/eager-loading.md +77 -0
- fastapi_viewsets-1.5.2/docs/getting-started.md +91 -0
- fastapi_viewsets-1.5.2/docs/index.md +130 -0
- fastapi_viewsets-1.5.2/docs/orm-adapters.md +262 -0
- fastapi_viewsets-1.5.2/docs/overrides.md +161 -0
- fastapi_viewsets-1.5.2/docs/pagination-filtering.md +145 -0
- fastapi_viewsets-1.5.2/docs/quickstart-async.md +115 -0
- fastapi_viewsets-1.5.2/docs/quickstart-sync.md +152 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/__init__.py +11 -5
- fastapi_viewsets-1.5.2/fastapi_viewsets/_register.py +245 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/async_base.py +12 -6
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/orm/peewee_adapter.py +12 -11
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/orm/sqlalchemy_adapter.py +46 -30
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/orm/tortoise_adapter.py +13 -11
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets.egg-info/PKG-INFO +68 -14
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets.egg-info/SOURCES.txt +23 -1
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets.egg-info/requires.txt +3 -3
- fastapi_viewsets-1.5.2/mkdocs.yml +115 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/pyproject.toml +4 -4
- fastapi_viewsets-1.5.2/pytest.ini +22 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/setup.cfg +0 -1
- fastapi_viewsets-1.5.2/tests/__init__.py +2 -0
- fastapi_viewsets-1.5.2/tests/conftest.py +210 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_async_utils.py +10 -10
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_edge_cases.py +11 -13
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_error_handling.py +1 -1
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_exception_handling.py +3 -3
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_integration.py +1 -1
- fastapi_viewsets-1.5.2/tests/test_quickstarts.py +98 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_utils.py +9 -9
- fastapi_viewsets-1.5.2/tests/test_write_path_regressions.py +447 -0
- fastapi_viewsets-1.5.2/tests/tortoise_models.py +28 -0
- fastapi_viewsets-1.5.0/fastapi_viewsets/_register.py +0 -133
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/LICENSE +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/_compat.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/async_utils.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/constants.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/db_conf.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/filtering.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/orm/__init__.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/orm/base.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/orm/factory.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/serializer_utils.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets/utils.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets.egg-info/dependency_links.txt +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/fastapi_viewsets.egg-info/top_level.txt +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/setup.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_adapter_methods_coverage.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_async_base_viewset.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_async_driver_missing.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_backward_compatibility.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_base_viewset.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_coverage_gaps.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_db_conf.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_db_conf_extended.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_missing_coverage.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_orm_adapters.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_orm_adapters_extended.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_search_ordering.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_select_prefetch_related.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_tortoise_lifecycle.py +0 -0
- {fastapi_viewsets-1.5.0 → fastapi_viewsets-1.5.2}/tests/test_viewsets_with_adapters.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi_viewsets
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.2
|
|
4
4
|
Summary: DRF-style viewsets for FastAPI with SQLAlchemy/Tortoise/Peewee adapters and Pydantic v2 support.
|
|
5
5
|
Author: Alexander Valenchits
|
|
6
6
|
License: MIT
|
|
@@ -25,12 +25,10 @@ Requires-Python: >=3.9
|
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
License-File: LICENSE
|
|
27
27
|
Requires-Dist: fastapi>=0.110.0
|
|
28
|
-
Requires-Dist: uvicorn>=0.17.6
|
|
29
|
-
Requires-Dist: SQLAlchemy>=1.4.36
|
|
30
28
|
Requires-Dist: pydantic<3,>=2.5
|
|
31
29
|
Requires-Dist: python-dotenv>=0.19.0
|
|
32
30
|
Provides-Extra: sqlalchemy
|
|
33
|
-
Requires-Dist: SQLAlchemy>=
|
|
31
|
+
Requires-Dist: SQLAlchemy[asyncio]>=2.0.0; extra == "sqlalchemy"
|
|
34
32
|
Provides-Extra: tortoise
|
|
35
33
|
Requires-Dist: tortoise-orm<1.0,>=0.20.0; extra == "tortoise"
|
|
36
34
|
Requires-Dist: asyncpg>=0.28.0; extra == "tortoise"
|
|
@@ -43,6 +41,8 @@ Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
|
43
41
|
Requires-Dist: httpx>=0.24.0; extra == "test"
|
|
44
42
|
Requires-Dist: faker>=18.0.0; extra == "test"
|
|
45
43
|
Requires-Dist: aiosqlite>=0.19.0; extra == "test"
|
|
44
|
+
Requires-Dist: uvicorn>=0.17.6; extra == "test"
|
|
45
|
+
Requires-Dist: SQLAlchemy[asyncio]>=2.0.0; extra == "test"
|
|
46
46
|
Provides-Extra: lint
|
|
47
47
|
Requires-Dist: ruff>=0.5; extra == "lint"
|
|
48
48
|
Requires-Dist: black>=24; extra == "lint"
|
|
@@ -95,15 +95,21 @@ Django REST Framework-style ViewSets for FastAPI — auto-generate CRUD endpoint
|
|
|
95
95
|
pip install fastapi-viewsets
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
The base install is ORM-agnostic: it pulls in only FastAPI, Pydantic and
|
|
99
|
+
python-dotenv. Pick your ORM via an extra (see `pyproject.toml`):
|
|
99
100
|
|
|
100
101
|
```bash
|
|
101
|
-
pip install "fastapi-viewsets[sqlalchemy]"
|
|
102
|
+
pip install "fastapi-viewsets[sqlalchemy]" # SQLAlchemy 2.x, incl. asyncio support (greenlet)
|
|
102
103
|
pip install "fastapi-viewsets[tortoise]"
|
|
103
104
|
pip install "fastapi-viewsets[peewee]"
|
|
104
105
|
pip install "fastapi-viewsets[test]" # pytest, httpx, coverage, etc.
|
|
105
106
|
```
|
|
106
107
|
|
|
108
|
+
For a local SQLite app, start with the
|
|
109
|
+
[sync quickstart](#quickstart-sqlalchemy-sync); no separate database driver is needed.
|
|
110
|
+
To execute the quickstart's `python main.py` (or serve any app) you also
|
|
111
|
+
need an ASGI server, e.g. `pip install uvicorn`.
|
|
112
|
+
|
|
107
113
|
For async SQLAlchemy you still need a driver such as `aiosqlite`, `asyncpg`, or `aiomysql` alongside your database URL.
|
|
108
114
|
|
|
109
115
|
## Database connection examples
|
|
@@ -432,6 +438,9 @@ an async-capable URL and use the lazy helpers from `db_conf`. The
|
|
|
432
438
|
package auto-converts `sqlite://` to `sqlite+aiosqlite://`,
|
|
433
439
|
`postgresql://` to `postgresql+asyncpg://`, etc.
|
|
434
440
|
|
|
441
|
+
Save the following as `main.py` in an empty folder, then run
|
|
442
|
+
`uvicorn main:app --reload`. Open `http://127.0.0.1:8000/docs` to try the API.
|
|
443
|
+
|
|
435
444
|
```python
|
|
436
445
|
from contextlib import asynccontextmanager
|
|
437
446
|
|
|
@@ -492,10 +501,23 @@ Notes:
|
|
|
492
501
|
`model_config = ConfigDict(from_attributes=True)` instead of the v1
|
|
493
502
|
`class Config: orm_mode = True`.
|
|
494
503
|
- `PATCH` uses `model_dump(exclude_unset=True)` internally, so unset
|
|
495
|
-
fields are no longer overwritten with defaults.
|
|
504
|
+
fields are no longer overwritten with defaults. Since v1.5.2 the PATCH
|
|
505
|
+
body is validated against an auto-generated all-optional variant of
|
|
506
|
+
your `response_model`, so partial bodies pass validation even when the
|
|
507
|
+
schema has required fields, and an explicit JSON `null` clears a
|
|
508
|
+
nullable column.
|
|
509
|
+
- `PUT` replaces exactly the fields present in the payload; model
|
|
510
|
+
columns that are not part of the `response_model` are left untouched.
|
|
511
|
+
- `POST` never sends an explicit `NULL` primary key to the database —
|
|
512
|
+
`id: Optional[int] = None` in the schema is safe on every adapter.
|
|
513
|
+
- Integrity violations (e.g. duplicate unique values) return
|
|
514
|
+
`409 Conflict` with a sanitized message; raw SQL and driver internals
|
|
515
|
+
are never exposed in the response body.
|
|
496
516
|
- If the async driver (`aiosqlite` / `asyncpg` / `aiomysql`) is not
|
|
497
517
|
installed, sync usage still works — only `get_async_session()` raises
|
|
498
|
-
a helpful `RuntimeError`.
|
|
518
|
+
a helpful `RuntimeError`. The `sqlalchemy` extra installs
|
|
519
|
+
`SQLAlchemy[asyncio]`, which already includes `greenlet` for async
|
|
520
|
+
sessions.
|
|
499
521
|
|
|
500
522
|
## Overriding `list` and `create_element` (custom LIST and POST)
|
|
501
523
|
|
|
@@ -503,8 +525,11 @@ Every CRUD handler is a regular method, so subclassing the viewset is
|
|
|
503
525
|
the canonical way to add filtering, ordering, validation, conflict
|
|
504
526
|
handling, and so on. The example below subclasses `AsyncBaseViewset`
|
|
505
527
|
and overrides both `list` (case-insensitive search + simple ordering)
|
|
506
|
-
and `create_element` (input normalization +
|
|
507
|
-
|
|
528
|
+
and `create_element` (input normalization + custom conflict message).
|
|
529
|
+
|
|
530
|
+
> Since v1.5.2 the adapters themselves map `IntegrityError` to
|
|
531
|
+
> `409 Conflict`; override `create_element` only when you need a custom
|
|
532
|
+
> error payload or extra normalization.
|
|
508
533
|
|
|
509
534
|
```python
|
|
510
535
|
from typing import List, Optional
|
|
@@ -670,7 +695,7 @@ app.include_router(router)
|
|
|
670
695
|
|
|
671
696
|
## Pagination, filtering, ordering
|
|
672
697
|
|
|
673
|
-
**Pagination** — `BaseViewset.list` maps `limit` and `offset` to query parameters on the LIST route.
|
|
698
|
+
**Pagination** — `BaseViewset.list` maps `limit` and `offset` to query parameters on the LIST route. Defaults are `limit=10`, `offset=0`; negative values are rejected with `422`, and `limit` is capped at `10000`.
|
|
674
699
|
|
|
675
700
|
```python
|
|
676
701
|
from fastapi_viewsets import BaseViewset
|
|
@@ -731,6 +756,34 @@ class ItemsWithStats(BaseViewset):
|
|
|
731
756
|
|
|
732
757
|
## What is new
|
|
733
758
|
|
|
759
|
+
### v1.5.2
|
|
760
|
+
|
|
761
|
+
Bugfix release for the CRUD write paths (see
|
|
762
|
+
[RELEASE_1.5.2.md](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_1.5.2.md)):
|
|
763
|
+
|
|
764
|
+
- **Multi-viewset apps fixed**: `register()` no longer leaks one viewset's
|
|
765
|
+
body schema into other viewsets — every `POST`/`PUT`/`PATCH` endpoint
|
|
766
|
+
now validates against its own `response_model`.
|
|
767
|
+
- **PATCH is truly partial**: the PATCH body is validated against an
|
|
768
|
+
auto-generated all-optional variant of the response schema, and an
|
|
769
|
+
explicit JSON `null` clears a nullable column.
|
|
770
|
+
- **PUT no longer nulls columns** that are absent from the Pydantic
|
|
771
|
+
schema.
|
|
772
|
+
- **Integrity violations return `409 Conflict`** with a sanitized message
|
|
773
|
+
instead of `400` with raw SQL/driver internals.
|
|
774
|
+
- **Create never passes an explicit `NULL` primary key** — fixes Tortoise
|
|
775
|
+
`POST` and PostgreSQL inserts with `id: Optional[int] = None` schemas.
|
|
776
|
+
- **Pagination validated**: negative `limit`/`offset` rejected with `422`.
|
|
777
|
+
- **Filters advertised in OpenAPI**: whitelisted `ListConfig.filters`
|
|
778
|
+
fields and their `__op` variants show up in Swagger UI.
|
|
779
|
+
- **Leaner dependencies**: the base install no longer requires SQLAlchemy
|
|
780
|
+
or uvicorn; the `sqlalchemy` extra installs `SQLAlchemy[asyncio]`
|
|
781
|
+
(includes `greenlet`).
|
|
782
|
+
|
|
783
|
+
Earlier releases: v1.5.0 (server-side `search`, declarative ordering and
|
|
784
|
+
filters — [RELEASE_1.5.0.md](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_1.5.0.md)),
|
|
785
|
+
v1.4.0 ([RELEASE_1.4.0.md](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_1.4.0.md)).
|
|
786
|
+
|
|
734
787
|
### v1.3.0
|
|
735
788
|
|
|
736
789
|
- **Declarative eager loading** via `RelatedConfig` inside Pydantic schemas.
|
|
@@ -746,9 +799,9 @@ class ItemsWithStats(BaseViewset):
|
|
|
746
799
|
- Internal `register()` deduplicated between sync and async viewsets via a shared mixin.
|
|
747
800
|
- PEP 621 `pyproject.toml`, `python_requires>=3.9`, FastAPI `>=0.110`, ruff/black/mypy preconfigured.
|
|
748
801
|
|
|
749
|
-
Previous release: [v1.1.0](RELEASE_1.1.0.md) introduced multi-ORM support via adapters (SQLAlchemy default, optional Tortoise and Peewee), `ORMFactory` and environment-driven `ORM_TYPE` configuration.
|
|
802
|
+
Previous release: [v1.1.0](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_1.1.0.md) introduced multi-ORM support via adapters (SQLAlchemy default, optional Tortoise and Peewee), `ORMFactory` and environment-driven `ORM_TYPE` configuration.
|
|
750
803
|
|
|
751
|
-
Details: [RELEASE_NOTES.md](RELEASE_NOTES.md), [RELEASE_1.2.0.md](RELEASE_1.2.0.md), [RELEASE_1.1.0.md](RELEASE_1.1.0.md).
|
|
804
|
+
Details: [RELEASE_NOTES.md](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_NOTES.md), [RELEASE_1.2.0.md](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_1.2.0.md), [RELEASE_1.1.0.md](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_1.1.0.md).
|
|
752
805
|
|
|
753
806
|
## Roadmap (planned)
|
|
754
807
|
|
|
@@ -773,6 +826,7 @@ Released: server-side `search` (v1.5.0), declarative ordering and advanced filte
|
|
|
773
826
|
From the repository root (see `pytest.ini`):
|
|
774
827
|
|
|
775
828
|
```bash
|
|
829
|
+
python -m pip install -e ".[test]"
|
|
776
830
|
pytest
|
|
777
831
|
```
|
|
778
832
|
|
|
@@ -784,7 +838,7 @@ See [open issues](https://github.com/svalench/fastapi_viewsets/issues) to propos
|
|
|
784
838
|
|
|
785
839
|
## License
|
|
786
840
|
|
|
787
|
-
Distributed under the MIT License. See [LICENSE](LICENSE).
|
|
841
|
+
Distributed under the MIT License. See [LICENSE](https://github.com/svalench/fastapi_viewsets/blob/master/LICENSE).
|
|
788
842
|
|
|
789
843
|
## Author
|
|
790
844
|
|
|
@@ -40,15 +40,21 @@ Django REST Framework-style ViewSets for FastAPI — auto-generate CRUD endpoint
|
|
|
40
40
|
pip install fastapi-viewsets
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
The base install is ORM-agnostic: it pulls in only FastAPI, Pydantic and
|
|
44
|
+
python-dotenv. Pick your ORM via an extra (see `pyproject.toml`):
|
|
44
45
|
|
|
45
46
|
```bash
|
|
46
|
-
pip install "fastapi-viewsets[sqlalchemy]"
|
|
47
|
+
pip install "fastapi-viewsets[sqlalchemy]" # SQLAlchemy 2.x, incl. asyncio support (greenlet)
|
|
47
48
|
pip install "fastapi-viewsets[tortoise]"
|
|
48
49
|
pip install "fastapi-viewsets[peewee]"
|
|
49
50
|
pip install "fastapi-viewsets[test]" # pytest, httpx, coverage, etc.
|
|
50
51
|
```
|
|
51
52
|
|
|
53
|
+
For a local SQLite app, start with the
|
|
54
|
+
[sync quickstart](#quickstart-sqlalchemy-sync); no separate database driver is needed.
|
|
55
|
+
To execute the quickstart's `python main.py` (or serve any app) you also
|
|
56
|
+
need an ASGI server, e.g. `pip install uvicorn`.
|
|
57
|
+
|
|
52
58
|
For async SQLAlchemy you still need a driver such as `aiosqlite`, `asyncpg`, or `aiomysql` alongside your database URL.
|
|
53
59
|
|
|
54
60
|
## Database connection examples
|
|
@@ -377,6 +383,9 @@ an async-capable URL and use the lazy helpers from `db_conf`. The
|
|
|
377
383
|
package auto-converts `sqlite://` to `sqlite+aiosqlite://`,
|
|
378
384
|
`postgresql://` to `postgresql+asyncpg://`, etc.
|
|
379
385
|
|
|
386
|
+
Save the following as `main.py` in an empty folder, then run
|
|
387
|
+
`uvicorn main:app --reload`. Open `http://127.0.0.1:8000/docs` to try the API.
|
|
388
|
+
|
|
380
389
|
```python
|
|
381
390
|
from contextlib import asynccontextmanager
|
|
382
391
|
|
|
@@ -437,10 +446,23 @@ Notes:
|
|
|
437
446
|
`model_config = ConfigDict(from_attributes=True)` instead of the v1
|
|
438
447
|
`class Config: orm_mode = True`.
|
|
439
448
|
- `PATCH` uses `model_dump(exclude_unset=True)` internally, so unset
|
|
440
|
-
fields are no longer overwritten with defaults.
|
|
449
|
+
fields are no longer overwritten with defaults. Since v1.5.2 the PATCH
|
|
450
|
+
body is validated against an auto-generated all-optional variant of
|
|
451
|
+
your `response_model`, so partial bodies pass validation even when the
|
|
452
|
+
schema has required fields, and an explicit JSON `null` clears a
|
|
453
|
+
nullable column.
|
|
454
|
+
- `PUT` replaces exactly the fields present in the payload; model
|
|
455
|
+
columns that are not part of the `response_model` are left untouched.
|
|
456
|
+
- `POST` never sends an explicit `NULL` primary key to the database —
|
|
457
|
+
`id: Optional[int] = None` in the schema is safe on every adapter.
|
|
458
|
+
- Integrity violations (e.g. duplicate unique values) return
|
|
459
|
+
`409 Conflict` with a sanitized message; raw SQL and driver internals
|
|
460
|
+
are never exposed in the response body.
|
|
441
461
|
- If the async driver (`aiosqlite` / `asyncpg` / `aiomysql`) is not
|
|
442
462
|
installed, sync usage still works — only `get_async_session()` raises
|
|
443
|
-
a helpful `RuntimeError`.
|
|
463
|
+
a helpful `RuntimeError`. The `sqlalchemy` extra installs
|
|
464
|
+
`SQLAlchemy[asyncio]`, which already includes `greenlet` for async
|
|
465
|
+
sessions.
|
|
444
466
|
|
|
445
467
|
## Overriding `list` and `create_element` (custom LIST and POST)
|
|
446
468
|
|
|
@@ -448,8 +470,11 @@ Every CRUD handler is a regular method, so subclassing the viewset is
|
|
|
448
470
|
the canonical way to add filtering, ordering, validation, conflict
|
|
449
471
|
handling, and so on. The example below subclasses `AsyncBaseViewset`
|
|
450
472
|
and overrides both `list` (case-insensitive search + simple ordering)
|
|
451
|
-
and `create_element` (input normalization +
|
|
452
|
-
|
|
473
|
+
and `create_element` (input normalization + custom conflict message).
|
|
474
|
+
|
|
475
|
+
> Since v1.5.2 the adapters themselves map `IntegrityError` to
|
|
476
|
+
> `409 Conflict`; override `create_element` only when you need a custom
|
|
477
|
+
> error payload or extra normalization.
|
|
453
478
|
|
|
454
479
|
```python
|
|
455
480
|
from typing import List, Optional
|
|
@@ -615,7 +640,7 @@ app.include_router(router)
|
|
|
615
640
|
|
|
616
641
|
## Pagination, filtering, ordering
|
|
617
642
|
|
|
618
|
-
**Pagination** — `BaseViewset.list` maps `limit` and `offset` to query parameters on the LIST route.
|
|
643
|
+
**Pagination** — `BaseViewset.list` maps `limit` and `offset` to query parameters on the LIST route. Defaults are `limit=10`, `offset=0`; negative values are rejected with `422`, and `limit` is capped at `10000`.
|
|
619
644
|
|
|
620
645
|
```python
|
|
621
646
|
from fastapi_viewsets import BaseViewset
|
|
@@ -676,6 +701,34 @@ class ItemsWithStats(BaseViewset):
|
|
|
676
701
|
|
|
677
702
|
## What is new
|
|
678
703
|
|
|
704
|
+
### v1.5.2
|
|
705
|
+
|
|
706
|
+
Bugfix release for the CRUD write paths (see
|
|
707
|
+
[RELEASE_1.5.2.md](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_1.5.2.md)):
|
|
708
|
+
|
|
709
|
+
- **Multi-viewset apps fixed**: `register()` no longer leaks one viewset's
|
|
710
|
+
body schema into other viewsets — every `POST`/`PUT`/`PATCH` endpoint
|
|
711
|
+
now validates against its own `response_model`.
|
|
712
|
+
- **PATCH is truly partial**: the PATCH body is validated against an
|
|
713
|
+
auto-generated all-optional variant of the response schema, and an
|
|
714
|
+
explicit JSON `null` clears a nullable column.
|
|
715
|
+
- **PUT no longer nulls columns** that are absent from the Pydantic
|
|
716
|
+
schema.
|
|
717
|
+
- **Integrity violations return `409 Conflict`** with a sanitized message
|
|
718
|
+
instead of `400` with raw SQL/driver internals.
|
|
719
|
+
- **Create never passes an explicit `NULL` primary key** — fixes Tortoise
|
|
720
|
+
`POST` and PostgreSQL inserts with `id: Optional[int] = None` schemas.
|
|
721
|
+
- **Pagination validated**: negative `limit`/`offset` rejected with `422`.
|
|
722
|
+
- **Filters advertised in OpenAPI**: whitelisted `ListConfig.filters`
|
|
723
|
+
fields and their `__op` variants show up in Swagger UI.
|
|
724
|
+
- **Leaner dependencies**: the base install no longer requires SQLAlchemy
|
|
725
|
+
or uvicorn; the `sqlalchemy` extra installs `SQLAlchemy[asyncio]`
|
|
726
|
+
(includes `greenlet`).
|
|
727
|
+
|
|
728
|
+
Earlier releases: v1.5.0 (server-side `search`, declarative ordering and
|
|
729
|
+
filters — [RELEASE_1.5.0.md](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_1.5.0.md)),
|
|
730
|
+
v1.4.0 ([RELEASE_1.4.0.md](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_1.4.0.md)).
|
|
731
|
+
|
|
679
732
|
### v1.3.0
|
|
680
733
|
|
|
681
734
|
- **Declarative eager loading** via `RelatedConfig` inside Pydantic schemas.
|
|
@@ -691,9 +744,9 @@ class ItemsWithStats(BaseViewset):
|
|
|
691
744
|
- Internal `register()` deduplicated between sync and async viewsets via a shared mixin.
|
|
692
745
|
- PEP 621 `pyproject.toml`, `python_requires>=3.9`, FastAPI `>=0.110`, ruff/black/mypy preconfigured.
|
|
693
746
|
|
|
694
|
-
Previous release: [v1.1.0](RELEASE_1.1.0.md) introduced multi-ORM support via adapters (SQLAlchemy default, optional Tortoise and Peewee), `ORMFactory` and environment-driven `ORM_TYPE` configuration.
|
|
747
|
+
Previous release: [v1.1.0](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_1.1.0.md) introduced multi-ORM support via adapters (SQLAlchemy default, optional Tortoise and Peewee), `ORMFactory` and environment-driven `ORM_TYPE` configuration.
|
|
695
748
|
|
|
696
|
-
Details: [RELEASE_NOTES.md](RELEASE_NOTES.md), [RELEASE_1.2.0.md](RELEASE_1.2.0.md), [RELEASE_1.1.0.md](RELEASE_1.1.0.md).
|
|
749
|
+
Details: [RELEASE_NOTES.md](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_NOTES.md), [RELEASE_1.2.0.md](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_1.2.0.md), [RELEASE_1.1.0.md](https://github.com/svalench/fastapi_viewsets/blob/master/RELEASE_1.1.0.md).
|
|
697
750
|
|
|
698
751
|
## Roadmap (planned)
|
|
699
752
|
|
|
@@ -718,6 +771,7 @@ Released: server-side `search` (v1.5.0), declarative ordering and advanced filte
|
|
|
718
771
|
From the repository root (see `pytest.ini`):
|
|
719
772
|
|
|
720
773
|
```bash
|
|
774
|
+
python -m pip install -e ".[test]"
|
|
721
775
|
pytest
|
|
722
776
|
```
|
|
723
777
|
|
|
@@ -729,7 +783,7 @@ See [open issues](https://github.com/svalench/fastapi_viewsets/issues) to propos
|
|
|
729
783
|
|
|
730
784
|
## License
|
|
731
785
|
|
|
732
|
-
Distributed under the MIT License. See [LICENSE](LICENSE).
|
|
786
|
+
Distributed under the MIT License. See [LICENSE](https://github.com/svalench/fastapi_viewsets/blob/master/LICENSE).
|
|
733
787
|
|
|
734
788
|
## Author
|
|
735
789
|
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
## `BaseViewset`
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from fastapi_viewsets import BaseViewset
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Synchronous CRUD viewset. Subclasses `APIRouter` and `_RegisterMixin`. Provides `LIST`, `GET`, `POST`, `PUT`, `PATCH`, and `DELETE` endpoints generated from an ORM model and a Pydantic response model.
|
|
10
|
+
|
|
11
|
+
### Constructor
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
BaseViewset(
|
|
15
|
+
*,
|
|
16
|
+
allowed_methods: Optional[List[str]] = None,
|
|
17
|
+
endpoint: Optional[str] = None,
|
|
18
|
+
model: Optional[Type[ModelType]] = None,
|
|
19
|
+
db_session: Optional[Callable[[], Any]] = None,
|
|
20
|
+
response_model: Optional[Type[BaseModel]] = None,
|
|
21
|
+
orm_adapter: Optional[BaseORMAdapter] = None,
|
|
22
|
+
**kwargs, # forwarded to APIRouter
|
|
23
|
+
)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
| Parameter | Type | Default | Description |
|
|
27
|
+
| --- | --- | --- | --- |
|
|
28
|
+
| `allowed_methods` | `Optional[List[str]]` | `None` | Override of `ALLOWED_METHODS` |
|
|
29
|
+
| `endpoint` | `Optional[str]` | `None` | Base endpoint path, e.g. `"/user"` |
|
|
30
|
+
| `model` | `Optional[Type]` | `None` | ORM model class |
|
|
31
|
+
| `db_session` | `Optional[Callable]` | `None` | Database session factory function |
|
|
32
|
+
| `response_model` | `Optional[Type[BaseModel]]` | `None` | Pydantic schema for request/response bodies |
|
|
33
|
+
| `orm_adapter` | `Optional[BaseORMAdapter]` | `None` | ORM adapter; resolved from config when omitted |
|
|
34
|
+
| `**kwargs` | — | — | Forwarded to `fastapi.APIRouter` (e.g. `tags`, `prefix`, `dependencies`) |
|
|
35
|
+
|
|
36
|
+
### CRUD handlers
|
|
37
|
+
|
|
38
|
+
#### `list()`
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
def list(
|
|
42
|
+
self,
|
|
43
|
+
limit: Optional[int] = 10,
|
|
44
|
+
offset: Optional[int] = 0,
|
|
45
|
+
search: Optional[str] = None,
|
|
46
|
+
token: str = Depends(_noop_dependency),
|
|
47
|
+
) -> List[ResponseModelType]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
List items with `limit`/`offset` pagination. Returns a list of `response_model` instances.
|
|
51
|
+
|
|
52
|
+
!!! warning "`search` parameter is reserved"
|
|
53
|
+
|
|
54
|
+
The `search` parameter is accepted by `list()` and appears in the
|
|
55
|
+
OpenAPI schema, but ORM adapters currently ignore it. Server-side
|
|
56
|
+
search is planned for v1.4. Until then, override `list()` in a
|
|
57
|
+
subclass to implement filtering — see
|
|
58
|
+
[Pagination & Filtering](pagination-filtering.md).
|
|
59
|
+
|
|
60
|
+
#### `get_element()`
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
def get_element(
|
|
64
|
+
self,
|
|
65
|
+
id: Union[int, str],
|
|
66
|
+
token: str = Depends(_noop_dependency),
|
|
67
|
+
) -> ResponseModelType
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Retrieve a single item by ID. Raises `404` if `id` is empty or `None`.
|
|
71
|
+
|
|
72
|
+
#### `create_element()`
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
def create_element(
|
|
76
|
+
self,
|
|
77
|
+
item: ResponseModelType = Body(...),
|
|
78
|
+
token: str = Depends(_noop_dependency),
|
|
79
|
+
) -> ResponseModelType
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Create a new item from the request body.
|
|
83
|
+
|
|
84
|
+
#### `update_element()`
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
def update_element(
|
|
88
|
+
self,
|
|
89
|
+
id: Union[int, str],
|
|
90
|
+
item: ResponseModelType = Body(...),
|
|
91
|
+
token: str = Depends(_noop_dependency),
|
|
92
|
+
partial: bool = False,
|
|
93
|
+
) -> ResponseModelType
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Update an existing item. `partial=True` (used by `PATCH`) only writes fields the client explicitly set, preserving correct PATCH semantics under Pydantic v2.
|
|
97
|
+
|
|
98
|
+
#### `delete_element()`
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
def delete_element(
|
|
102
|
+
self,
|
|
103
|
+
id: Union[int, str],
|
|
104
|
+
token: str = Depends(_noop_dependency),
|
|
105
|
+
) -> Dict[str, Union[bool, str]]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Delete an item by ID. Returns `{"status": True, "text": "successfully deleted"}` or `{"status": False, "text": "deletion failed"}`.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## `AsyncBaseViewset`
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from fastapi_viewsets import AsyncBaseViewset
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Asynchronous CRUD viewset. Mirrors `BaseViewset` but every CRUD handler is `async`, backed by an async-capable ORM adapter (SQLAlchemy `AsyncSession` or Tortoise ORM).
|
|
119
|
+
|
|
120
|
+
### Constructor
|
|
121
|
+
|
|
122
|
+
Same parameters as `BaseViewset`, but `db_session` should be an async session factory (`Callable[[], AsyncSession]`).
|
|
123
|
+
|
|
124
|
+
### CRUD handlers
|
|
125
|
+
|
|
126
|
+
Identical signatures to `BaseViewset`, but `async def`:
|
|
127
|
+
|
|
128
|
+
- `async def list(...)`
|
|
129
|
+
- `async def get_element(...)`
|
|
130
|
+
- `async def create_element(...)`
|
|
131
|
+
- `async def update_element(...)`
|
|
132
|
+
- `async def delete_element(...)`
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## `register()`
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
def register(
|
|
140
|
+
self,
|
|
141
|
+
methods: Optional[List[str]] = None,
|
|
142
|
+
oauth_protect: Optional[OAuth2PasswordBearer] = None,
|
|
143
|
+
protected_methods: Optional[List[str]] = None,
|
|
144
|
+
) -> None
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Register CRUD endpoints on the router.
|
|
148
|
+
|
|
149
|
+
| Parameter | Type | Default | Description |
|
|
150
|
+
| --- | --- | --- | --- |
|
|
151
|
+
| `methods` | `Optional[List[str]]` | `None` (all) | Logical methods to register |
|
|
152
|
+
| `oauth_protect` | `Optional[OAuth2PasswordBearer]` | `None` | OAuth2 dependency for protected operations |
|
|
153
|
+
| `protected_methods` | `Optional[List[str]]` | `None` | Subset of `methods` requiring the bearer token |
|
|
154
|
+
|
|
155
|
+
### Allowed methods
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
ALLOWED_METHODS = ["LIST", "POST", "GET", "PUT", "PATCH", "DELETE"]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Method-to-route mapping
|
|
162
|
+
|
|
163
|
+
| Logical method | HTTP method | Path | Handler | Response |
|
|
164
|
+
| --- | --- | --- | --- | --- |
|
|
165
|
+
| `LIST` | `GET` | `/` | `list` | `List[response_model]` |
|
|
166
|
+
| `GET` | `GET` | `/{id}` | `get_element` | `response_model` |
|
|
167
|
+
| `POST` | `POST` | `/` | `create_element` | `response_model` |
|
|
168
|
+
| `PUT` | `PUT` | `/{id}` | `update_element` (`partial=False`) | `response_model` |
|
|
169
|
+
| `PATCH` | `PATCH` | `/{id}` | `update_element` (`partial=True`) | `response_model` |
|
|
170
|
+
| `DELETE` | `DELETE` | `/{id}` | `delete_element` | `None` (raw dict) |
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## `ORMFactory`
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from fastapi_viewsets.orm.factory import ORMFactory
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Central entry point for adapter resolution.
|
|
181
|
+
|
|
182
|
+
### Methods
|
|
183
|
+
|
|
184
|
+
| Method | Returns | Description |
|
|
185
|
+
| --- | --- | --- |
|
|
186
|
+
| `get_default_adapter()` | `BaseORMAdapter` | Returns the cached singleton adapter for the current `ORM_TYPE` |
|
|
187
|
+
| `create_adapter(orm_type, config)` | `BaseORMAdapter` | Creates a new adapter instance from a dict config |
|
|
188
|
+
| `register_adapter(orm_type, adapter_class)` | `None` | Registers a custom adapter class (e.g. for a new ORM) |
|
|
189
|
+
| `get_adapter_from_env()` | `BaseORMAdapter` | Builds an adapter from environment variables (used internally by `get_default_adapter`) |
|
|
190
|
+
| `reset_default_adapter()` | `None` | Clears the cached singleton; the next `get_default_adapter()` call rebuilds from env. Useful in tests and hot-reload scenarios. |
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## `BaseORMAdapter`
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from fastapi_viewsets.orm.base import BaseORMAdapter
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Abstract base class for ORM adapters. All adapters (SQLAlchemy, Tortoise, Peewee) implement this interface.
|
|
201
|
+
|
|
202
|
+
### Abstract methods
|
|
203
|
+
|
|
204
|
+
| Method | Sync/Async | Description |
|
|
205
|
+
| --- | --- | --- |
|
|
206
|
+
| `get_session()` | Sync | Get a synchronous database session |
|
|
207
|
+
| `get_async_session()` | Sync | Get an async database session |
|
|
208
|
+
| `get_base()` | Sync | Get the base class for ORM models |
|
|
209
|
+
| `get_list_queryset(model, db_session, limit, offset, select_related, prefetch_related)` | Sync | Get a paginated list of model instances |
|
|
210
|
+
| `get_list_queryset_async(model, db_session, limit, offset, select_related, prefetch_related)` | Async | Async version of `get_list_queryset` |
|
|
211
|
+
| `get_element_by_id(model, db_session, id, select_related, prefetch_related)` | Sync | Get a single element by ID |
|
|
212
|
+
| `get_element_by_id_async(model, db_session, id, select_related, prefetch_related)` | Async | Async version of `get_element_by_id` |
|
|
213
|
+
| `create_element(model, db_session, data)` | Sync | Create a new element |
|
|
214
|
+
| `create_element_async(model, db_session, data)` | Async | Async version of `create_element` |
|
|
215
|
+
| `update_element(model, db_session, id, data, partial)` | Sync | Update an element (`partial=True` for PATCH) |
|
|
216
|
+
| `update_element_async(model, db_session, id, data, partial)` | Async | Async version of `update_element` |
|
|
217
|
+
| `delete_element(model, db_session, id)` | Sync | Delete an element by ID |
|
|
218
|
+
| `delete_element_async(model, db_session, id)` | Async | Async version of `delete_element` |
|
|
219
|
+
| `get_model_columns(model)` | Sync | Get column information for a model |
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## `db_conf` module
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from fastapi_viewsets.db_conf import (
|
|
227
|
+
ORM_TYPE,
|
|
228
|
+
SQLALCHEMY_DATABASE_URL,
|
|
229
|
+
get_orm_adapter,
|
|
230
|
+
# Lazy SQLAlchemy globals:
|
|
231
|
+
engine,
|
|
232
|
+
Base,
|
|
233
|
+
SessionLocal,
|
|
234
|
+
db_session,
|
|
235
|
+
get_session,
|
|
236
|
+
async_engine,
|
|
237
|
+
AsyncSessionLocal,
|
|
238
|
+
get_async_session,
|
|
239
|
+
)
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Environment variables
|
|
243
|
+
|
|
244
|
+
| Variable | Default | Description |
|
|
245
|
+
| --- | --- | --- |
|
|
246
|
+
| `ORM_TYPE` | `sqlalchemy` | Active ORM: `sqlalchemy`, `tortoise`, or `peewee` |
|
|
247
|
+
| `DATABASE_URL` | `sqlite:///<cwd>/base.db` | Generic database URL (fallback) |
|
|
248
|
+
| `SQLALCHEMY_DATABASE_URL` | — | SQLAlchemy-specific URL |
|
|
249
|
+
| `SQLALCHEMY_ASYNC_DATABASE_URL` | Auto-derived | Explicit async URL (overrides auto-conversion) |
|
|
250
|
+
| `TORTOISE_DATABASE_URL` | — | Tortoise database URL |
|
|
251
|
+
| `TORTOISE_MODELS` | — | JSON list of model modules, e.g. `["app.models"]` |
|
|
252
|
+
| `TORTOISE_APP_LABEL` | `models` | Tortoise app label |
|
|
253
|
+
| `PEEWEE_DATABASE_URL` | — | Peewee database URL |
|
|
254
|
+
|
|
255
|
+
### Lazy resolution
|
|
256
|
+
|
|
257
|
+
SQLAlchemy globals (`engine`, `Base`, `get_session`, `get_async_session`, etc.) are resolved lazily on first access. Importing the package does not create engines unless they are needed, and works without async drivers installed.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Serializer utilities
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
from fastapi_viewsets.serializer_utils import get_select_related, get_prefetch_related
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
These helpers read eager-loading configuration from a Pydantic schema's inner `RelatedConfig` class. They are used internally by `get_list_queryset` and `get_element_by_id` (sync and async) when a `response_model` is provided.
|
|
268
|
+
|
|
269
|
+
| Function | Returns | Description |
|
|
270
|
+
| --- | --- | --- |
|
|
271
|
+
| `get_select_related(response_model)` | `List[str]` | Reads `RelatedConfig.select_related` (FK / many-to-one relations) |
|
|
272
|
+
| `get_prefetch_related(response_model)` | `List[str]` | Reads `RelatedConfig.prefetch_related` (collections / M2M relations) |
|
|
273
|
+
|
|
274
|
+
See [Eager Loading](eager-loading.md) for usage examples.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Internal auth placeholder
|
|
279
|
+
|
|
280
|
+
The `token` parameter on every CRUD handler defaults to `Depends(_noop_dependency)`, where `_noop_dependency` is a private function that returns `None`. It is overridden when `oauth_protect` is passed to `register()`. The backward-compatible alias `butle` also points to this function.
|
|
281
|
+
|
|
282
|
+
You should not import `_noop_dependency` directly. If you override a handler and want to preserve the optional-auth behaviour, use `Depends(lambda: None)` or your own no-op dependency.
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Constants
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
from fastapi_viewsets.constants import ALLOWED_METHODS, MAP_METHODS
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### `ALLOWED_METHODS`
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
ALLOWED_METHODS = ["LIST", "POST", "GET", "PUT", "PATCH", "DELETE"]
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### `MAP_METHODS`
|
|
299
|
+
|
|
300
|
+
Maps logical method names to their route specifications:
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
MAP_METHODS = {
|
|
304
|
+
"GET": {"method": "get_element", "http_method": "GET", "path": "/{id}", "is_list": False},
|
|
305
|
+
"POST": {"method": "create_element", "http_method": "POST", "path": "", "is_list": False},
|
|
306
|
+
"PUT": {"method": "update_element", "http_method": "PUT", "path": "/{id}", "is_list": False},
|
|
307
|
+
"PATCH": {"method": "update_element", "http_method": "PATCH", "path": "/{id}", "is_list": False},
|
|
308
|
+
"DELETE": {"method": "delete_element", "http_method": "DELETE", "path": "/{id}", "is_list": False},
|
|
309
|
+
"LIST": {"method": "list", "http_method": "GET", "path": "", "is_list": True},
|
|
310
|
+
}
|
|
311
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* fastapi-viewsets docs custom styles */
|
|
2
|
+
|
|
3
|
+
.md-header__title {
|
|
4
|
+
font-weight: 700;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.md-typeset__table td:not(:last-child),
|
|
8
|
+
.md-typeset__table th:not(:last-child) {
|
|
9
|
+
padding-right: 1.2rem;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.md-typeset .grid.cards > ul > li {
|
|
13
|
+
border: 1px solid var(--md-default-fg-color--lightest);
|
|
14
|
+
border-radius: 0.4rem;
|
|
15
|
+
transition: border-color 0.25s, box-shadow 0.25s;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.md-typeset .grid.cards > ul > li:hover {
|
|
19
|
+
border-color: var(--md-accent-fg-color);
|
|
20
|
+
box-shadow: 0 0 0.2rem var(--md-accent-fg-color);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.md-typeset code {
|
|
24
|
+
font-size: 0.85em;
|
|
25
|
+
}
|