ohmyapi 0.1.24__tar.gz → 0.1.26__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.
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/PKG-INFO +36 -16
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/README.md +29 -12
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/pyproject.toml +4 -4
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/builtin/auth/routes.py +2 -2
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/builtin/demo/routes.py +9 -9
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/core/runtime.py +7 -4
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/core/templates/app/routes.py.j2 +10 -7
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/core/templates/project/settings.py.j2 +1 -1
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/db/model/model.py +5 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/__init__.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/__main__.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/builtin/auth/__init__.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/builtin/auth/models.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/builtin/auth/permissions.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/builtin/demo/__init__.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/builtin/demo/models.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/cli.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/core/__init__.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/core/scaffolding.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/core/templates/app/__init__.py.j2 +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/core/templates/app/models.py.j2 +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/core/templates/project/README.md.j2 +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/core/templates/project/pyproject.toml.j2 +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/db/__init__.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/db/exceptions.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/db/model/__init__.py +0 -0
- {ohmyapi-0.1.24 → ohmyapi-0.1.26}/src/ohmyapi/router.py +0 -0
@@ -1,13 +1,16 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ohmyapi
|
3
|
-
Version: 0.1.
|
4
|
-
Summary:
|
3
|
+
Version: 0.1.26
|
4
|
+
Summary: Django-flavored scaffolding and management layer around FastAPI, Pydantic, TortoiseORM and Aerich migrations
|
5
5
|
License-Expression: MIT
|
6
|
-
Keywords: fastapi,tortoise,orm,async,web-framework
|
6
|
+
Keywords: fastapi,tortoise,orm,pydantic,async,web-framework
|
7
7
|
Author: Brian Wiborg
|
8
8
|
Author-email: me@brianwib.org
|
9
|
-
Requires-Python: >=3.
|
9
|
+
Requires-Python: >=3.10
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
11
14
|
Classifier: Programming Language :: Python :: 3.13
|
12
15
|
Classifier: Programming Language :: Python :: 3.14
|
13
16
|
Provides-Extra: auth
|
@@ -62,7 +65,7 @@ It is ***blazingly fast***, extremely ***fun to use*** and comes with ***batteri
|
|
62
65
|
**Creating a Project**
|
63
66
|
|
64
67
|
```
|
65
|
-
|
68
|
+
pipx install ohmyapi
|
66
69
|
ohmyapi startproject myproject
|
67
70
|
cd myproject
|
68
71
|
```
|
@@ -157,33 +160,50 @@ Next, create your endpoints in `tournament/routes.py`:
|
|
157
160
|
from ohmyapi.router import APIRouter, HTTPException, HTTPStatus
|
158
161
|
from ohmyapi.db.exceptions import DoesNotExist
|
159
162
|
|
163
|
+
from typing import List
|
164
|
+
|
160
165
|
from .models import Tournament
|
161
166
|
|
162
|
-
#
|
163
|
-
#
|
167
|
+
# OhMyAPI will automatically pick up all instances of `fastapi.APIRouter` and
|
168
|
+
# add their routes to the main project router.
|
169
|
+
#
|
170
|
+
# Note:
|
171
|
+
# Use prefixes wisely to avoid cross-app namespace-collisions!
|
164
172
|
# Tags improve the UX of the OpenAPI docs at /docs.
|
165
|
-
|
173
|
+
#
|
174
|
+
tournament_router = APIRouter(prefix="/tournament", tags=['Tournament'])
|
166
175
|
|
167
|
-
|
176
|
+
|
177
|
+
@tournament_router.get("/", response_model=List[Tournament.Schema()])
|
168
178
|
async def list():
|
169
179
|
queryset = Tournament.all()
|
170
180
|
return await Tournament.Schema.model.from_queryset(queryset)
|
171
181
|
|
172
182
|
|
173
|
-
@
|
174
|
-
async def post(tournament: Tournament.Schema
|
183
|
+
@tournament_router.post("/", status_code=HTTPStatus.CREATED)
|
184
|
+
async def post(tournament: Tournament.Schema(readonly=True)):
|
175
185
|
queryset = Tournament.create(**payload.model_dump())
|
176
|
-
return await Tournament.Schema.
|
186
|
+
return await Tournament.Schema().from_queryset(queryset)
|
177
187
|
|
178
188
|
|
179
|
-
@
|
189
|
+
@tournament_router.get("/:id", response_model=Tournament.Schema())
|
180
190
|
async def get(id: str):
|
181
191
|
try:
|
182
192
|
queryset = Tournament.get(id=id)
|
183
|
-
return await Tournament.Schema.
|
193
|
+
return await Tournament.Schema().from_queryset_single(tournament)
|
194
|
+
except DoesNotExist:
|
195
|
+
raise HTTPException(status_code=404, detail="not found")
|
196
|
+
|
197
|
+
|
198
|
+
@tournament_router.delete("/:id")
|
199
|
+
async def delete(id: str):
|
200
|
+
try:
|
201
|
+
tournament = await Tournament.get(id=id)
|
202
|
+
return await Tournament.Schema.model.from_queryset(tournament.delete())
|
184
203
|
except DoesNotExist:
|
185
204
|
raise HTTPException(status_code=404, detail="not found")
|
186
205
|
|
206
|
+
|
187
207
|
...
|
188
208
|
```
|
189
209
|
|
@@ -290,7 +310,7 @@ router = APIRouter(prefix="/tournament", tags=["Tournament"])
|
|
290
310
|
@router.get("/")
|
291
311
|
async def list(user: auth.User = Depends(permissions.require_authenticated)):
|
292
312
|
queryset = Tournament.all()
|
293
|
-
return await Tournament.Schema.
|
313
|
+
return await Tournament.Schema().from_queryset(queryset)
|
294
314
|
|
295
315
|
|
296
316
|
...
|
@@ -332,7 +352,7 @@ router = APIRouter(prefix="/tournament", tags=["Tournament"])
|
|
332
352
|
@router.get("/teams")
|
333
353
|
async def teams(user: auth.User = Depends(permissions.require_authenticated)):
|
334
354
|
queryset = Team.for_user(user)
|
335
|
-
return await Tournament.Schema.
|
355
|
+
return await Tournament.Schema().from_queryset(queryset)
|
336
356
|
```
|
337
357
|
|
338
358
|
## Shell
|
@@ -30,7 +30,7 @@ It is ***blazingly fast***, extremely ***fun to use*** and comes with ***batteri
|
|
30
30
|
**Creating a Project**
|
31
31
|
|
32
32
|
```
|
33
|
-
|
33
|
+
pipx install ohmyapi
|
34
34
|
ohmyapi startproject myproject
|
35
35
|
cd myproject
|
36
36
|
```
|
@@ -125,33 +125,50 @@ Next, create your endpoints in `tournament/routes.py`:
|
|
125
125
|
from ohmyapi.router import APIRouter, HTTPException, HTTPStatus
|
126
126
|
from ohmyapi.db.exceptions import DoesNotExist
|
127
127
|
|
128
|
+
from typing import List
|
129
|
+
|
128
130
|
from .models import Tournament
|
129
131
|
|
130
|
-
#
|
131
|
-
#
|
132
|
+
# OhMyAPI will automatically pick up all instances of `fastapi.APIRouter` and
|
133
|
+
# add their routes to the main project router.
|
134
|
+
#
|
135
|
+
# Note:
|
136
|
+
# Use prefixes wisely to avoid cross-app namespace-collisions!
|
132
137
|
# Tags improve the UX of the OpenAPI docs at /docs.
|
133
|
-
|
138
|
+
#
|
139
|
+
tournament_router = APIRouter(prefix="/tournament", tags=['Tournament'])
|
134
140
|
|
135
|
-
|
141
|
+
|
142
|
+
@tournament_router.get("/", response_model=List[Tournament.Schema()])
|
136
143
|
async def list():
|
137
144
|
queryset = Tournament.all()
|
138
145
|
return await Tournament.Schema.model.from_queryset(queryset)
|
139
146
|
|
140
147
|
|
141
|
-
@
|
142
|
-
async def post(tournament: Tournament.Schema
|
148
|
+
@tournament_router.post("/", status_code=HTTPStatus.CREATED)
|
149
|
+
async def post(tournament: Tournament.Schema(readonly=True)):
|
143
150
|
queryset = Tournament.create(**payload.model_dump())
|
144
|
-
return await Tournament.Schema.
|
151
|
+
return await Tournament.Schema().from_queryset(queryset)
|
145
152
|
|
146
153
|
|
147
|
-
@
|
154
|
+
@tournament_router.get("/:id", response_model=Tournament.Schema())
|
148
155
|
async def get(id: str):
|
149
156
|
try:
|
150
157
|
queryset = Tournament.get(id=id)
|
151
|
-
return await Tournament.Schema.
|
158
|
+
return await Tournament.Schema().from_queryset_single(tournament)
|
159
|
+
except DoesNotExist:
|
160
|
+
raise HTTPException(status_code=404, detail="not found")
|
161
|
+
|
162
|
+
|
163
|
+
@tournament_router.delete("/:id")
|
164
|
+
async def delete(id: str):
|
165
|
+
try:
|
166
|
+
tournament = await Tournament.get(id=id)
|
167
|
+
return await Tournament.Schema.model.from_queryset(tournament.delete())
|
152
168
|
except DoesNotExist:
|
153
169
|
raise HTTPException(status_code=404, detail="not found")
|
154
170
|
|
171
|
+
|
155
172
|
...
|
156
173
|
```
|
157
174
|
|
@@ -258,7 +275,7 @@ router = APIRouter(prefix="/tournament", tags=["Tournament"])
|
|
258
275
|
@router.get("/")
|
259
276
|
async def list(user: auth.User = Depends(permissions.require_authenticated)):
|
260
277
|
queryset = Tournament.all()
|
261
|
-
return await Tournament.Schema.
|
278
|
+
return await Tournament.Schema().from_queryset(queryset)
|
262
279
|
|
263
280
|
|
264
281
|
...
|
@@ -300,7 +317,7 @@ router = APIRouter(prefix="/tournament", tags=["Tournament"])
|
|
300
317
|
@router.get("/teams")
|
301
318
|
async def teams(user: auth.User = Depends(permissions.require_authenticated)):
|
302
319
|
queryset = Team.for_user(user)
|
303
|
-
return await Tournament.Schema.
|
320
|
+
return await Tournament.Schema().from_queryset(queryset)
|
304
321
|
```
|
305
322
|
|
306
323
|
## Shell
|
@@ -1,14 +1,14 @@
|
|
1
1
|
[project]
|
2
2
|
name = "ohmyapi"
|
3
|
-
version = "0.1.
|
4
|
-
description = "
|
3
|
+
version = "0.1.26"
|
4
|
+
description = "Django-flavored scaffolding and management layer around FastAPI, Pydantic, TortoiseORM and Aerich migrations"
|
5
5
|
license = "MIT"
|
6
|
-
keywords = ["fastapi", "tortoise", "orm", "async", "web-framework"]
|
6
|
+
keywords = ["fastapi", "tortoise", "orm", "pydantic", "async", "web-framework"]
|
7
7
|
authors = [
|
8
8
|
{name = "Brian Wiborg", email = "me@brianwib.org"}
|
9
9
|
]
|
10
10
|
readme = "README.md"
|
11
|
-
requires-python = ">=3.
|
11
|
+
requires-python = ">=3.10"
|
12
12
|
|
13
13
|
dependencies = [
|
14
14
|
"typer >=0.19.1,<0.20.0",
|
@@ -207,7 +207,7 @@ async def introspect(token: Dict = Depends(get_token)):
|
|
207
207
|
return token
|
208
208
|
|
209
209
|
|
210
|
-
@router.get("/me", response_model=User.Schema
|
210
|
+
@router.get("/me", response_model=User.Schema())
|
211
211
|
async def me(user: User = Depends(get_current_user)):
|
212
212
|
"""Return the currently authenticated user."""
|
213
|
-
return await User.Schema.
|
213
|
+
return await User.Schema().from_tortoise_orm(user)
|
@@ -8,29 +8,29 @@ from typing import List
|
|
8
8
|
# Expose your app's routes via `router = fastapi.APIRouter`.
|
9
9
|
# Use prefixes wisely to avoid cross-app namespace-collisions.
|
10
10
|
# Tags improve the UX of the OpenAPI docs at /docs.
|
11
|
-
router = APIRouter(prefix="/
|
11
|
+
router = APIRouter(prefix="/tournament")
|
12
12
|
|
13
13
|
|
14
14
|
@router.get(
|
15
|
-
"/", tags=["tournament"], response_model=List[models.Tournament.Schema
|
15
|
+
"/", tags=["tournament"], response_model=List[models.Tournament.Schema()]
|
16
16
|
)
|
17
17
|
async def list():
|
18
18
|
"""List all tournaments."""
|
19
|
-
return await models.Tournament.Schema.
|
19
|
+
return await models.Tournament.Schema().from_queryset(models.Tournament.all())
|
20
20
|
|
21
21
|
|
22
22
|
@router.post("/", tags=["tournament"], status_code=HTTPStatus.CREATED)
|
23
|
-
async def post(tournament: models.Tournament.Schema
|
23
|
+
async def post(tournament: models.Tournament.Schema(readonly=True)):
|
24
24
|
"""Create tournament."""
|
25
|
-
return await models.Tournament.Schema.
|
25
|
+
return await models.Tournament.Schema().from_queryset(
|
26
26
|
models.Tournament.create(**tournament.model_dump())
|
27
27
|
)
|
28
28
|
|
29
29
|
|
30
|
-
@router.get("/{id}", tags=["tournament"], response_model=models.Tournament.Schema
|
30
|
+
@router.get("/{id}", tags=["tournament"], response_model=models.Tournament.Schema())
|
31
31
|
async def get(id: str):
|
32
32
|
"""Get tournament by id."""
|
33
|
-
return await models.Tournament.Schema.
|
33
|
+
return await models.Tournament.Schema().from_queryset(
|
34
34
|
models.Tournament.get(id=id)
|
35
35
|
)
|
36
36
|
|
@@ -43,12 +43,12 @@ async def get(id: str):
|
|
43
43
|
)
|
44
44
|
async def put(tournament: models.Tournament.Schema.model):
|
45
45
|
"""Update tournament."""
|
46
|
-
return await models.Tournament.Schema.
|
46
|
+
return await models.Tournament.Schema().from_queryset(
|
47
47
|
models.Tournament.update(**tournament.model_dump())
|
48
48
|
)
|
49
49
|
|
50
50
|
|
51
|
-
@router.delete("/{id}", tags=["tournament"])
|
51
|
+
@router.delete("/{id}", status_code=HTTPStatus.ACCEPTED, tags=["tournament"])
|
52
52
|
async def delete(id: str):
|
53
53
|
try:
|
54
54
|
tournament = await models.Tournament.get(id=id)
|
@@ -211,7 +211,7 @@ class App:
|
|
211
211
|
self.model_modules: List[str] = []
|
212
212
|
|
213
213
|
# The APIRouter
|
214
|
-
self.router:
|
214
|
+
self.router: APIRouter = APIRouter()
|
215
215
|
|
216
216
|
# Import the app, so its __init__.py runs.
|
217
217
|
importlib.import_module(self.name)
|
@@ -226,9 +226,12 @@ class App:
|
|
226
226
|
# Locate the APIRouter
|
227
227
|
try:
|
228
228
|
routes_mod = importlib.import_module(f"{self.name}.routes")
|
229
|
-
|
230
|
-
|
231
|
-
|
229
|
+
for attr_name in dir(routes_mod):
|
230
|
+
if attr_name.startswith("__"):
|
231
|
+
continue
|
232
|
+
attr = getattr(routes_mod, attr_name)
|
233
|
+
if isinstance(attr, APIRouter):
|
234
|
+
self.router.include_router(attr)
|
232
235
|
except ModuleNotFoundError:
|
233
236
|
pass
|
234
237
|
|
@@ -4,23 +4,26 @@ from . import models
|
|
4
4
|
|
5
5
|
from typing import List
|
6
6
|
|
7
|
-
#
|
8
|
-
#
|
7
|
+
# OhMyAPI will automatically pick up all instances of `fastapi.APIRouter` and
|
8
|
+
# add their routes to the main project router.
|
9
|
+
#
|
10
|
+
# Note:
|
11
|
+
# Use prefixes wisely to avoid cross-app namespace-collisions!
|
9
12
|
# Tags improve the UX of the OpenAPI docs at /docs.
|
13
|
+
#
|
10
14
|
router = APIRouter(prefix="/{{ app_name }}", tags=['{{ app_name }}'])
|
11
15
|
|
12
16
|
|
13
|
-
|
14
|
-
@router.get("/")
|
17
|
+
@router.get("/", response_model=List)
|
15
18
|
async def list():
|
16
19
|
"""List all ..."""
|
17
20
|
return []
|
18
21
|
|
19
22
|
|
20
|
-
@router.post("/")
|
23
|
+
@router.post("/", status_code=HTTPStatus.CREATED)
|
21
24
|
async def post():
|
22
25
|
"""Create ..."""
|
23
|
-
|
26
|
+
raise HTTPException(status_code=HTTPStatus.IM_A_TEAPOT)
|
24
27
|
|
25
28
|
|
26
29
|
@router.get("/{id}")
|
@@ -35,7 +38,7 @@ async def put(id: str):
|
|
35
38
|
return HTTPException(status_code=HTTPStatus.ACCEPTED)
|
36
39
|
|
37
40
|
|
38
|
-
@router.delete("/{id}")
|
41
|
+
@router.delete("/{id}", status_code=HTTPStatus.ACCEPTED)
|
39
42
|
async def delete(id: str):
|
40
43
|
return HTTPException(status_code=HTTPStatus.ACCEPTED)
|
41
44
|
|
@@ -36,6 +36,11 @@ class ModelMeta(type(TortoiseModel)):
|
|
36
36
|
schema_opts = getattr(new_cls, "Schema", None)
|
37
37
|
|
38
38
|
class BoundSchema:
|
39
|
+
def __call__(self, readonly: bool = False):
|
40
|
+
if readonly:
|
41
|
+
return self.readonly
|
42
|
+
return self.model
|
43
|
+
|
39
44
|
@property
|
40
45
|
def model(self):
|
41
46
|
"""Return a Pydantic model class for serializing results."""
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|