fastapi-crud-generator 0.0.1__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_crud_generator-0.0.1/.github/workflows/docs.yml +25 -0
- fastapi_crud_generator-0.0.1/.github/workflows/release.yml +32 -0
- fastapi_crud_generator-0.0.1/.github/workflows/tests.yml +105 -0
- fastapi_crud_generator-0.0.1/.gitignore +38 -0
- fastapi_crud_generator-0.0.1/LICENSE.txt +21 -0
- fastapi_crud_generator-0.0.1/PKG-INFO +109 -0
- fastapi_crud_generator-0.0.1/README.md +56 -0
- fastapi_crud_generator-0.0.1/docs/api-reference.md +37 -0
- fastapi_crud_generator-0.0.1/docs/custom-adapter.md +120 -0
- fastapi_crud_generator-0.0.1/docs/guide/customization.md +70 -0
- fastapi_crud_generator-0.0.1/docs/guide/dependencies.md +58 -0
- fastapi_crud_generator-0.0.1/docs/guide/filtering-sorting.md +69 -0
- fastapi_crud_generator-0.0.1/docs/guide/getting-started.md +109 -0
- fastapi_crud_generator-0.0.1/docs/guide/nested-resources.md +74 -0
- fastapi_crud_generator-0.0.1/docs/guide/orm-adapters.md +102 -0
- fastapi_crud_generator-0.0.1/docs/guide/pagination.md +50 -0
- fastapi_crud_generator-0.0.1/docs/guide/schema-generation.md +105 -0
- fastapi_crud_generator-0.0.1/docs/index.md +62 -0
- fastapi_crud_generator-0.0.1/examples/__init__.py +0 -0
- fastapi_crud_generator-0.0.1/examples/forum/__init__.py +0 -0
- fastapi_crud_generator-0.0.1/examples/forum/orm/__init__.py +0 -0
- fastapi_crud_generator-0.0.1/examples/forum/orm/sqlalchemy.py +101 -0
- fastapi_crud_generator-0.0.1/examples/forum/orm/sqlmodel.py +106 -0
- fastapi_crud_generator-0.0.1/examples/forum/orm/tortoise.py +77 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/__init__.py +9 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/config.py +50 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/crud_generator.py +587 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/deps.py +275 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/orm/__init__.py +0 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/orm/base.py +169 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/orm/sqlalchemy.py +551 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/orm/sqlmodel.py +504 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/orm/tortoise.py +521 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/paginator.py +47 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/schemas.py +43 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/strategies.py +144 -0
- fastapi_crud_generator-0.0.1/fastapi_crud_generator/utils.py +138 -0
- fastapi_crud_generator-0.0.1/mkdocs.yml +62 -0
- fastapi_crud_generator-0.0.1/pyproject.toml +85 -0
- fastapi_crud_generator-0.0.1/tests/__init__.py +0 -0
- fastapi_crud_generator-0.0.1/tests/integration/__init__.py +0 -0
- fastapi_crud_generator-0.0.1/tests/integration/backends/__init__.py +12 -0
- fastapi_crud_generator-0.0.1/tests/integration/backends/sqlalchemy.py +73 -0
- fastapi_crud_generator-0.0.1/tests/integration/backends/sqlmodel.py +81 -0
- fastapi_crud_generator-0.0.1/tests/integration/backends/tortoise.py +113 -0
- fastapi_crud_generator-0.0.1/tests/integration/conftest.py +222 -0
- fastapi_crud_generator-0.0.1/tests/integration/test_basic.py +51 -0
- fastapi_crud_generator-0.0.1/tests/integration/test_composite.py +102 -0
- fastapi_crud_generator-0.0.1/tests/integration/test_include.py +251 -0
- fastapi_crud_generator-0.0.1/tests/integration/test_nested.py +129 -0
- fastapi_crud_generator-0.0.1/tests/integration/test_root_posts.py +65 -0
- fastapi_crud_generator-0.0.1/tests/integration/test_user_posts.py +78 -0
- fastapi_crud_generator-0.0.1/tests/test_deps.py +384 -0
- fastapi_crud_generator-0.0.1/tests/test_sqlmodel_crud_collection_pk.py +93 -0
- fastapi_crud_generator-0.0.1/tests/test_sqlmodel_schema_generation.py +202 -0
- fastapi_crud_generator-0.0.1/tests/test_utils_filter_model.py +73 -0
- fastapi_crud_generator-0.0.1/tests/test_utils_model_builders.py +59 -0
- fastapi_crud_generator-0.0.1/tests/test_utils_pydantic_field.py +82 -0
- fastapi_crud_generator-0.0.1/tests/test_utils_sort_schema.py +75 -0
- fastapi_crud_generator-0.0.1/uv.lock +1345 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
paths:
|
|
8
|
+
- "docs/**"
|
|
9
|
+
- "mkdocs.yml"
|
|
10
|
+
- "fastapi_crud_generator/**" # docstrings in source
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
deploy:
|
|
14
|
+
name: Build & deploy
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
permissions:
|
|
17
|
+
contents: write # push to gh-pages branch
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- uses: astral-sh/setup-uv@v5
|
|
23
|
+
|
|
24
|
+
- name: Deploy docs
|
|
25
|
+
run: uv run --group docs mkdocs gh-deploy --force
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
release:
|
|
10
|
+
name: Build & publish
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
environment: pypi
|
|
13
|
+
permissions:
|
|
14
|
+
contents: write # create GitHub release
|
|
15
|
+
id-token: write # OIDC token for PyPI trusted publisher
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- uses: astral-sh/setup-uv@v5
|
|
21
|
+
|
|
22
|
+
- name: Build
|
|
23
|
+
run: uv build
|
|
24
|
+
|
|
25
|
+
- name: Create GitHub Release
|
|
26
|
+
uses: softprops/action-gh-release@v2
|
|
27
|
+
with:
|
|
28
|
+
generate_release_notes: true
|
|
29
|
+
files: dist/*
|
|
30
|
+
|
|
31
|
+
- name: Publish to PyPI
|
|
32
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
unit:
|
|
10
|
+
name: unit tests
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v5
|
|
15
|
+
- name: Install dependencies
|
|
16
|
+
run: uv sync --group dev --group sqlmodel
|
|
17
|
+
- name: Run unit tests
|
|
18
|
+
run: uv run pytest tests/ --ignore=tests/integration/ -q
|
|
19
|
+
|
|
20
|
+
test:
|
|
21
|
+
name: ${{ matrix.orm }} / ${{ matrix.db }}
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
|
|
24
|
+
services:
|
|
25
|
+
postgres:
|
|
26
|
+
image: postgres:16
|
|
27
|
+
env:
|
|
28
|
+
POSTGRES_PASSWORD: postgres
|
|
29
|
+
POSTGRES_DB: test
|
|
30
|
+
ports:
|
|
31
|
+
- 5432:5432
|
|
32
|
+
options: >-
|
|
33
|
+
--health-cmd pg_isready
|
|
34
|
+
--health-interval 10s
|
|
35
|
+
--health-timeout 5s
|
|
36
|
+
--health-retries 5
|
|
37
|
+
|
|
38
|
+
mysql:
|
|
39
|
+
image: mysql:8
|
|
40
|
+
env:
|
|
41
|
+
MYSQL_ROOT_PASSWORD: mysql
|
|
42
|
+
MYSQL_DATABASE: test
|
|
43
|
+
ports:
|
|
44
|
+
- 3306:3306
|
|
45
|
+
options: >-
|
|
46
|
+
--health-cmd "mysqladmin ping -pmysql"
|
|
47
|
+
--health-interval 10s
|
|
48
|
+
--health-timeout 5s
|
|
49
|
+
--health-retries 5
|
|
50
|
+
|
|
51
|
+
strategy:
|
|
52
|
+
fail-fast: false
|
|
53
|
+
matrix:
|
|
54
|
+
include:
|
|
55
|
+
- orm: sqlmodel
|
|
56
|
+
db: sqlite
|
|
57
|
+
uv_groups: "--group sqlmodel --group sqlite"
|
|
58
|
+
- orm: sqlalchemy
|
|
59
|
+
db: sqlite
|
|
60
|
+
uv_groups: "--group sqlalchemy --group sqlite"
|
|
61
|
+
- orm: tortoise
|
|
62
|
+
db: sqlite
|
|
63
|
+
uv_groups: "--group tortoise --group sqlite"
|
|
64
|
+
|
|
65
|
+
- orm: sqlmodel
|
|
66
|
+
db: postgres
|
|
67
|
+
uv_groups: "--group sqlmodel --group postgresql"
|
|
68
|
+
postgres_url: postgresql+asyncpg://postgres:postgres@localhost/test
|
|
69
|
+
- orm: sqlalchemy
|
|
70
|
+
db: postgres
|
|
71
|
+
uv_groups: "--group sqlalchemy --group postgresql"
|
|
72
|
+
postgres_url: postgresql+asyncpg://postgres:postgres@localhost/test
|
|
73
|
+
- orm: tortoise
|
|
74
|
+
db: postgres
|
|
75
|
+
uv_groups: "--group tortoise --group postgresql"
|
|
76
|
+
postgres_url: postgresql+asyncpg://postgres:postgres@localhost/test
|
|
77
|
+
|
|
78
|
+
- orm: sqlmodel
|
|
79
|
+
db: mysql
|
|
80
|
+
uv_groups: "--group sqlmodel --group mysql"
|
|
81
|
+
mysql_url: mysql+aiomysql://root:mysql@localhost/test
|
|
82
|
+
- orm: sqlalchemy
|
|
83
|
+
db: mysql
|
|
84
|
+
uv_groups: "--group sqlalchemy --group mysql"
|
|
85
|
+
mysql_url: mysql+aiomysql://root:mysql@localhost/test
|
|
86
|
+
- orm: tortoise
|
|
87
|
+
db: mysql
|
|
88
|
+
uv_groups: "--group tortoise --group mysql"
|
|
89
|
+
mysql_url: mysql+aiomysql://root:mysql@localhost/test
|
|
90
|
+
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/checkout@v4
|
|
93
|
+
|
|
94
|
+
- uses: astral-sh/setup-uv@v5
|
|
95
|
+
|
|
96
|
+
- name: Install dependencies
|
|
97
|
+
run: uv sync --group dev ${{ matrix.uv_groups }}
|
|
98
|
+
|
|
99
|
+
- name: Run tests
|
|
100
|
+
run: uv run pytest tests/integration/ -q
|
|
101
|
+
env:
|
|
102
|
+
PYTEST_ORM: ${{ matrix.orm }}
|
|
103
|
+
PYTEST_DB: ${{ matrix.db }}
|
|
104
|
+
POSTGRES_URL: ${{ matrix.postgres_url }}
|
|
105
|
+
MYSQL_URL: ${{ matrix.mysql_url }}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
.Python
|
|
7
|
+
|
|
8
|
+
# Virtual environments
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
env/
|
|
12
|
+
|
|
13
|
+
# Build / distribution
|
|
14
|
+
dist/
|
|
15
|
+
build/
|
|
16
|
+
*.egg-info/
|
|
17
|
+
*.egg
|
|
18
|
+
|
|
19
|
+
# uv
|
|
20
|
+
.uv/
|
|
21
|
+
|
|
22
|
+
# pytest
|
|
23
|
+
.pytest_cache/
|
|
24
|
+
.coverage
|
|
25
|
+
htmlcov/
|
|
26
|
+
|
|
27
|
+
# IDE
|
|
28
|
+
.vscode/
|
|
29
|
+
.idea/
|
|
30
|
+
*.swp
|
|
31
|
+
|
|
32
|
+
# Jupyter
|
|
33
|
+
jupyter/
|
|
34
|
+
*.ipynb
|
|
35
|
+
|
|
36
|
+
# MkDocs
|
|
37
|
+
site/
|
|
38
|
+
TEST_MATRIX.md
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nikolai Lukianov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-crud-generator
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: CRUD route generator for FastAPI
|
|
5
|
+
Project-URL: Homepage, https://github.com/mozgsml/fastapi_crud_generator
|
|
6
|
+
Project-URL: Repository, https://github.com/mozgsml/fastapi_crud_generator
|
|
7
|
+
Project-URL: Issues, https://github.com/mozgsml/fastapi_crud_generator/issues
|
|
8
|
+
Author-email: Nikolai Lukianov <laserwargpt@gmail.com>
|
|
9
|
+
License: The MIT License (MIT)
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 Nikolai Lukianov
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in
|
|
21
|
+
all copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
29
|
+
THE SOFTWARE.
|
|
30
|
+
License-File: LICENSE.txt
|
|
31
|
+
Keywords: api,crud,fastapi,rest,sqlalchemy,sqlmodel,tortoise
|
|
32
|
+
Classifier: Development Status :: 3 - Alpha
|
|
33
|
+
Classifier: Framework :: FastAPI
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
40
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
41
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
42
|
+
Classifier: Typing :: Typed
|
|
43
|
+
Requires-Python: >=3.11
|
|
44
|
+
Requires-Dist: fastapi
|
|
45
|
+
Requires-Dist: pydantic
|
|
46
|
+
Provides-Extra: sqlalchemy
|
|
47
|
+
Requires-Dist: sqlalchemy[asyncio]; extra == 'sqlalchemy'
|
|
48
|
+
Provides-Extra: sqlmodel
|
|
49
|
+
Requires-Dist: sqlmodel; extra == 'sqlmodel'
|
|
50
|
+
Provides-Extra: tortoise
|
|
51
|
+
Requires-Dist: tortoise-orm>=1.1.7; extra == 'tortoise'
|
|
52
|
+
Description-Content-Type: text/markdown
|
|
53
|
+
|
|
54
|
+
# fastapi-crud-generator
|
|
55
|
+
|
|
56
|
+
[](https://github.com/mozgsml/fastapi_crud_generator/actions/workflows/tests.yml)
|
|
57
|
+
[](https://pypi.org/project/fastapi-crud-generator/)
|
|
58
|
+
[](https://pypi.org/project/fastapi-crud-generator/)
|
|
59
|
+
[](LICENSE.txt)
|
|
60
|
+
|
|
61
|
+
Adds full CRUD routes to FastAPI without writing schemas, filters, or pagination by hand.
|
|
62
|
+
Everything is generated automatically from your ORM model.
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from fastapi_crud_generator import CRUDCollection
|
|
66
|
+
from fastapi_crud_generator.orm.sqlmodel import SQLModelAdapter
|
|
67
|
+
|
|
68
|
+
crud = CRUDCollection(orm_adapter=SQLModelAdapter(model=Article, get_session=get_session))
|
|
69
|
+
app.include_router(crud.get_router(prefix="/articles", tags=["articles"]))
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Three lines — and five endpoints are ready with filtering, sorting, and pagination:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
GET /articles ?title=…&published=true&sort=created_at:desc&page=1&per_page=20
|
|
76
|
+
GET /articles/{article_id}
|
|
77
|
+
POST /articles
|
|
78
|
+
PATCH /articles/{article_id}
|
|
79
|
+
DELETE /articles/{article_id}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
If SQLModel, SQLAlchemy, or Tortoise is already in your project:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install fastapi-crud-generator
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
To install an ORM together with the package:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pip install "fastapi-crud-generator[sqlmodel]"
|
|
94
|
+
pip install "fastapi-crud-generator[sqlalchemy]"
|
|
95
|
+
pip install "fastapi-crud-generator[tortoise]"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Documentation
|
|
99
|
+
|
|
100
|
+
**[mozgsml.github.io/fastapi_crud_generator](https://mozgsml.github.io/fastapi_crud_generator)**
|
|
101
|
+
|
|
102
|
+
- [Getting Started](https://mozgsml.github.io/fastapi_crud_generator/guide/getting-started/) — full working example from scratch
|
|
103
|
+
- [Schema Generation](https://mozgsml.github.io/fastapi_crud_generator/guide/schema-generation/) — control which fields appear in each schema
|
|
104
|
+
- [Nested Resources](https://mozgsml.github.io/fastapi_crud_generator/guide/nested-resources/) — `/threads/{id}/posts/{id}`
|
|
105
|
+
- [Custom Adapter](https://mozgsml.github.io/fastapi_crud_generator/custom-adapter/) — connect your own ORM
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
MIT
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# fastapi-crud-generator
|
|
2
|
+
|
|
3
|
+
[](https://github.com/mozgsml/fastapi_crud_generator/actions/workflows/tests.yml)
|
|
4
|
+
[](https://pypi.org/project/fastapi-crud-generator/)
|
|
5
|
+
[](https://pypi.org/project/fastapi-crud-generator/)
|
|
6
|
+
[](LICENSE.txt)
|
|
7
|
+
|
|
8
|
+
Adds full CRUD routes to FastAPI without writing schemas, filters, or pagination by hand.
|
|
9
|
+
Everything is generated automatically from your ORM model.
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from fastapi_crud_generator import CRUDCollection
|
|
13
|
+
from fastapi_crud_generator.orm.sqlmodel import SQLModelAdapter
|
|
14
|
+
|
|
15
|
+
crud = CRUDCollection(orm_adapter=SQLModelAdapter(model=Article, get_session=get_session))
|
|
16
|
+
app.include_router(crud.get_router(prefix="/articles", tags=["articles"]))
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Three lines — and five endpoints are ready with filtering, sorting, and pagination:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
GET /articles ?title=…&published=true&sort=created_at:desc&page=1&per_page=20
|
|
23
|
+
GET /articles/{article_id}
|
|
24
|
+
POST /articles
|
|
25
|
+
PATCH /articles/{article_id}
|
|
26
|
+
DELETE /articles/{article_id}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
If SQLModel, SQLAlchemy, or Tortoise is already in your project:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install fastapi-crud-generator
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
To install an ORM together with the package:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install "fastapi-crud-generator[sqlmodel]"
|
|
41
|
+
pip install "fastapi-crud-generator[sqlalchemy]"
|
|
42
|
+
pip install "fastapi-crud-generator[tortoise]"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Documentation
|
|
46
|
+
|
|
47
|
+
**[mozgsml.github.io/fastapi_crud_generator](https://mozgsml.github.io/fastapi_crud_generator)**
|
|
48
|
+
|
|
49
|
+
- [Getting Started](https://mozgsml.github.io/fastapi_crud_generator/guide/getting-started/) — full working example from scratch
|
|
50
|
+
- [Schema Generation](https://mozgsml.github.io/fastapi_crud_generator/guide/schema-generation/) — control which fields appear in each schema
|
|
51
|
+
- [Nested Resources](https://mozgsml.github.io/fastapi_crud_generator/guide/nested-resources/) — `/threads/{id}/posts/{id}`
|
|
52
|
+
- [Custom Adapter](https://mozgsml.github.io/fastapi_crud_generator/custom-adapter/) — connect your own ORM
|
|
53
|
+
|
|
54
|
+
## License
|
|
55
|
+
|
|
56
|
+
MIT
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
## CRUDCollection
|
|
4
|
+
|
|
5
|
+
::: fastapi_crud_generator.crud_generator.CRUDCollection
|
|
6
|
+
|
|
7
|
+
::: fastapi_crud_generator.crud_generator.CRUDCollectionBase
|
|
8
|
+
|
|
9
|
+
## Adapters
|
|
10
|
+
|
|
11
|
+
::: fastapi_crud_generator.orm.sqlmodel.SQLModelAdapter
|
|
12
|
+
|
|
13
|
+
::: fastapi_crud_generator.orm.sqlalchemy.SQLAlchemyAdapter
|
|
14
|
+
|
|
15
|
+
::: fastapi_crud_generator.orm.tortoise.TortoiseAdapter
|
|
16
|
+
|
|
17
|
+
::: fastapi_crud_generator.orm.base.ORMAdapterBase
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
::: fastapi_crud_generator.config.CRUDConfigDict
|
|
22
|
+
|
|
23
|
+
::: fastapi_crud_generator.config.NestedConfig
|
|
24
|
+
|
|
25
|
+
## Pagination
|
|
26
|
+
|
|
27
|
+
::: fastapi_crud_generator.paginator.PaginatorBase
|
|
28
|
+
|
|
29
|
+
::: fastapi_crud_generator.schemas.PaginatorPage
|
|
30
|
+
|
|
31
|
+
## Schemas & Errors
|
|
32
|
+
|
|
33
|
+
::: fastapi_crud_generator.schemas.ParentRef
|
|
34
|
+
|
|
35
|
+
::: fastapi_crud_generator.schemas.NotFoundError
|
|
36
|
+
|
|
37
|
+
::: fastapi_crud_generator.schemas.ParentNotFoundError
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Custom Adapter
|
|
2
|
+
|
|
3
|
+
To connect an ORM or data source that has no built-in adapter, implement `ORMAdapterBase`.
|
|
4
|
+
|
|
5
|
+
## Interface
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from fastapi_crud_generator.orm.base import ORMAdapterBase
|
|
9
|
+
from fastapi_crud_generator.schemas import PaginatorPage, ParentRef
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
|
|
12
|
+
class MyAdapter(ORMAdapterBase):
|
|
13
|
+
# Schema generation
|
|
14
|
+
def generate_public_schema(self, fields=None, base_fields=None) -> type[BaseModel]: ...
|
|
15
|
+
def generate_create_schema(self, fields=None, base_fields=None, exclude_related=None) -> type[BaseModel]: ...
|
|
16
|
+
def generate_update_schema(self, fields=None, base_fields=None) -> type[BaseModel]: ...
|
|
17
|
+
def generate_pk_schema(self) -> type[BaseModel]: ...
|
|
18
|
+
def generate_include_schema(self) -> type[BaseModel]: ...
|
|
19
|
+
|
|
20
|
+
# CRUD operations
|
|
21
|
+
async def get_one(self, pk_values, include_data, parent_refs=None): ...
|
|
22
|
+
async def get_many(self, filter_data, sort_data, include_data, paginator, parent_refs=None): ...
|
|
23
|
+
async def create_one(self, data, parent_refs=None): ...
|
|
24
|
+
async def update_one(self, pk_values, data, parent_refs=None): ...
|
|
25
|
+
async def delete_one(self, pk_values, parent_refs=None): ...
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Example: in-memory store
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from pydantic import BaseModel, create_model
|
|
32
|
+
from fastapi_crud_generator.orm.base import ORMAdapterBase
|
|
33
|
+
from fastapi_crud_generator.schemas import NotFoundError
|
|
34
|
+
|
|
35
|
+
_store: dict[int, dict] = {}
|
|
36
|
+
_next_id = 1
|
|
37
|
+
|
|
38
|
+
class ItemPublic(BaseModel):
|
|
39
|
+
id: int
|
|
40
|
+
name: str
|
|
41
|
+
price: float
|
|
42
|
+
|
|
43
|
+
class ItemCreate(BaseModel):
|
|
44
|
+
name: str
|
|
45
|
+
price: float
|
|
46
|
+
|
|
47
|
+
class ItemUpdate(BaseModel):
|
|
48
|
+
name: str | None = None
|
|
49
|
+
price: float | None = None
|
|
50
|
+
|
|
51
|
+
class ItemPK(BaseModel):
|
|
52
|
+
id: int
|
|
53
|
+
|
|
54
|
+
class InMemoryAdapter(ORMAdapterBase):
|
|
55
|
+
def generate_public_schema(self, fields=None, base_fields=None):
|
|
56
|
+
return ItemPublic
|
|
57
|
+
|
|
58
|
+
def generate_create_schema(self, fields=None, base_fields=None, exclude_related=None):
|
|
59
|
+
return ItemCreate
|
|
60
|
+
|
|
61
|
+
def generate_update_schema(self, fields=None, base_fields=None):
|
|
62
|
+
return ItemUpdate
|
|
63
|
+
|
|
64
|
+
def generate_pk_schema(self):
|
|
65
|
+
return ItemPK
|
|
66
|
+
|
|
67
|
+
def generate_include_schema(self):
|
|
68
|
+
return create_model("ItemInclude")
|
|
69
|
+
|
|
70
|
+
async def get_one(self, pk_values, include_data, parent_refs=None):
|
|
71
|
+
return _store.get(pk_values.id)
|
|
72
|
+
|
|
73
|
+
async def get_many(self, filter_data, sort_data, include_data, paginator, parent_refs=None):
|
|
74
|
+
items = list(_store.values())
|
|
75
|
+
page = items[paginator.offset : paginator.offset + paginator.limit]
|
|
76
|
+
return {"page": paginator.page, "per_page": paginator.per_page, "count": len(items), "data": page}
|
|
77
|
+
|
|
78
|
+
async def create_one(self, data, parent_refs=None):
|
|
79
|
+
global _next_id
|
|
80
|
+
item = {"id": _next_id, **data.model_dump()}
|
|
81
|
+
_store[_next_id] = item
|
|
82
|
+
_next_id += 1
|
|
83
|
+
return item
|
|
84
|
+
|
|
85
|
+
async def update_one(self, pk_values, data, parent_refs=None):
|
|
86
|
+
if pk_values.id not in _store:
|
|
87
|
+
raise NotFoundError
|
|
88
|
+
for k, v in data.model_dump(exclude_none=True).items():
|
|
89
|
+
_store[pk_values.id][k] = v
|
|
90
|
+
|
|
91
|
+
async def delete_one(self, pk_values, parent_refs=None):
|
|
92
|
+
return _store.pop(pk_values.id, None)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Usage:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
crud = CRUDCollection(orm_adapter=InMemoryAdapter())
|
|
99
|
+
app.include_router(crud.get_router(prefix="/items"))
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## `parent_refs` in nested resources
|
|
103
|
+
|
|
104
|
+
In nested routes, CRUD methods receive `parent_refs` — a list of `ParentRef`
|
|
105
|
+
objects describing the parent items in the URL:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from fastapi_crud_generator.schemas import ParentRef
|
|
109
|
+
|
|
110
|
+
async def get_many(self, filter_data, sort_data, include_data, paginator, parent_refs=None):
|
|
111
|
+
query = select(Post)
|
|
112
|
+
for ref in (parent_refs or []):
|
|
113
|
+
if ref.model is Thread:
|
|
114
|
+
query = query.where(Post.thread_id == ref.pk_values.thread_id)
|
|
115
|
+
...
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Full interface reference
|
|
119
|
+
|
|
120
|
+
See [API Reference](api-reference.md).
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Customization
|
|
2
|
+
|
|
3
|
+
## Disabling endpoints
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
crud = CRUDCollection(
|
|
7
|
+
orm_adapter=adapter,
|
|
8
|
+
disable_delete=True,
|
|
9
|
+
disable_create=True,
|
|
10
|
+
)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Overriding handlers via subclass
|
|
14
|
+
|
|
15
|
+
Subclass `CRUDCollection` and override the methods you need:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from fastapi_crud_generator import CRUDCollection
|
|
19
|
+
from fastapi_crud_generator.orm.sqlmodel import SQLModelAdapter
|
|
20
|
+
|
|
21
|
+
class ArticleCRUD(CRUDCollection):
|
|
22
|
+
orm_adapter = SQLModelAdapter(model=Article, get_session=get_session)
|
|
23
|
+
|
|
24
|
+
async def get_one_not_found(self, pk_values, include_data):
|
|
25
|
+
raise HTTPException(status_code=404, detail="Article not found")
|
|
26
|
+
|
|
27
|
+
async def create_one_handler(self, create_data, parent_refs):
|
|
28
|
+
create_data.slug = slugify(create_data.title)
|
|
29
|
+
return await super().create_one_handler(create_data, parent_refs)
|
|
30
|
+
|
|
31
|
+
crud = ArticleCRUD()
|
|
32
|
+
app.include_router(crud.get_router(prefix="/articles"))
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Class attributes instead of constructor arguments
|
|
36
|
+
|
|
37
|
+
All `CRUDCollection` parameters can be set as class attributes:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
class ArticleCRUD(CRUDCollection):
|
|
41
|
+
orm_adapter = SQLModelAdapter(model=Article, get_session=get_session)
|
|
42
|
+
disable_delete = True
|
|
43
|
+
dependencies = [Depends(require_auth)]
|
|
44
|
+
create_dependencies = [Depends(require_admin)]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This is handy when several collections share common configuration:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
class AuthenticatedCRUD(CRUDCollection):
|
|
51
|
+
dependencies = [Depends(require_auth)]
|
|
52
|
+
|
|
53
|
+
class ArticleCRUD(AuthenticatedCRUD):
|
|
54
|
+
orm_adapter = SQLModelAdapter(model=Article, get_session=get_session)
|
|
55
|
+
|
|
56
|
+
class CommentCRUD(AuthenticatedCRUD):
|
|
57
|
+
orm_adapter = SQLModelAdapter(model=Comment, get_session=get_session)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Custom ID path
|
|
61
|
+
|
|
62
|
+
By default, `CRUDCollection` builds a path like `/{article_id}` from the model name.
|
|
63
|
+
Override `apply_pk_aliases` to change this:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
class ArticleCRUD(CRUDCollection):
|
|
67
|
+
def apply_pk_aliases(self, pk_schema):
|
|
68
|
+
# use /{id} instead of /{article_id}
|
|
69
|
+
return pk_schema
|
|
70
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
|
|
3
|
+
Standard FastAPI dependencies (`Depends`) can be applied to all routes at once
|
|
4
|
+
or to a specific operation type.
|
|
5
|
+
|
|
6
|
+
## All routes
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
from fastapi import Depends
|
|
10
|
+
from fastapi_crud_generator import CRUDCollection
|
|
11
|
+
|
|
12
|
+
crud = CRUDCollection(
|
|
13
|
+
orm_adapter=adapter,
|
|
14
|
+
dependencies=[Depends(require_auth)],
|
|
15
|
+
)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Per operation type
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
crud = CRUDCollection(
|
|
22
|
+
orm_adapter=adapter,
|
|
23
|
+
get_one_dependencies=[Depends(require_auth)],
|
|
24
|
+
get_many_dependencies=[Depends(require_auth)],
|
|
25
|
+
create_dependencies=[Depends(require_admin)],
|
|
26
|
+
update_dependencies=[Depends(require_admin)],
|
|
27
|
+
delete_dependencies=[Depends(require_admin)],
|
|
28
|
+
)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Example: JWT authentication
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from fastapi import Depends, HTTPException, Security
|
|
35
|
+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
36
|
+
|
|
37
|
+
security = HTTPBearer()
|
|
38
|
+
|
|
39
|
+
async def require_auth(credentials: HTTPAuthorizationCredentials = Security(security)):
|
|
40
|
+
if not verify_token(credentials.credentials):
|
|
41
|
+
raise HTTPException(status_code=401, detail="Unauthorized")
|
|
42
|
+
|
|
43
|
+
crud = CRUDCollection(
|
|
44
|
+
orm_adapter=adapter,
|
|
45
|
+
dependencies=[Depends(require_auth)],
|
|
46
|
+
)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## At router level
|
|
50
|
+
|
|
51
|
+
Dependencies can also be added when mounting the router rather than in `CRUDCollection`:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
app.include_router(
|
|
55
|
+
crud.get_router(prefix="/articles"),
|
|
56
|
+
dependencies=[Depends(require_auth)],
|
|
57
|
+
)
|
|
58
|
+
```
|