Skip to content

Commit 48e5541

Browse files
authored
Merge pull request #60 from DorskFR/dependencies_routes
Dependencies can be added to each separate route (list of dependencies)
2 parents 2869887 + f81a759 commit 48e5541

24 files changed

+392
-186
lines changed

.flake8

Lines changed: 0 additions & 5 deletions
This file was deleted.

docs/en/docs/dependencies.md

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ All the CRUDRouters included with `fastapi_crudrouter` support FastAPI dependenc
44
Since all CRUDRouter's subclass the [FastAPI APIRouter](https://fastapi.tiangolo.com/tutorial/bigger-applications/?h=+router#apirouter),
55
you can use any features APIRouter features.
66

7-
## Example
87
Below is a simple example of how you could use OAuth2 in conjunction with a CRUDRouter to secure your routes.
98

109
```python
11-
from fastapi import FastAPI, testclient, Depends
10+
from fastapi import FastAPI, Depends, HTTPException
1211
from fastapi.security import OAuth2PasswordBearer
1312
from fastapi_crudrouter import MemoryCRUDRouter
1413

@@ -19,6 +18,45 @@ def token_auth(token: str):
1918
if not token:
2019
raise HTTPException(401, "Invalid token")
2120

22-
router = MemoryCRUDRouter(schema=Potato, dependencies=[Depends(token_auth)])
21+
router = MemoryCRUDRouter(schema=MySchema, dependencies=[Depends(token_auth)])
2322
app.include_router(router)
24-
```
23+
```
24+
25+
## Custom Dependencies Per Route
26+
All CRUDRouters allow you to add a sequence of dependencies on a per-route basis. The dependencies can be set when
27+
initializing any CRUDRouter using the key word arguments below.
28+
29+
```python
30+
CRUDRouter(
31+
# ...
32+
get_all_route=[Depends(get_all_route_dep), ...],
33+
get_one_route=[Depends(get_one_route_dep), ...],
34+
create_route=[Depends(create_route_dep), ...],
35+
update_route=[Depends(update_route_dep), ...],
36+
delete_one_route=[Depends(user), ...],
37+
delete_all_route=[Depends(user), ...],
38+
)
39+
40+
```
41+
42+
!!! tip "Multiple Dependencies Per Route"
43+
As they are passed as a sequence, you are able to set multiple dependencies individually per route.
44+
45+
!!! attention "Disabling Routes Entirely"
46+
Setting the key word arguments shown above to `False`, disables the route entirely.
47+
48+
49+
### Example
50+
In the example below, we are adding a fictitious dependency to the "create route" (POST) which requires the user to be
51+
logged in to create an object. At the same time, we are also independently adding an admin dependency to only the "delete all
52+
route" which limits the route's usage to only admin users.
53+
54+
```python
55+
MemoryCRUDRouter(
56+
schema=MySchema,
57+
create_route=[Depends(user)],
58+
delete_all_route=[Depends(admin)]
59+
)
60+
```
61+
62+

docs/en/docs/routing.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ As an example, the *delete all* route can be disabled by doing the following:
4646
router = MemmoryCRUDRouter(schema=MyModel, deleta_all_route=False)
4747
```
4848

49+
!!! tip "Custom Dependencies"
50+
Instead to passing a bool to the arguments listed about, you can also pass a sequence of custom dependencies to be
51+
applied to each route. See the docs on [dependencies](dependencies.md) for more details.
52+
53+
4954
## Overriding Routes
5055
Should you need to add custom functionality to any of your routes any of the included routers allows you to do so.
5156
Should you wish to disable a route from being generated, it can be done [here](../routing/#disabling-routes).

docs/en/mkdocs.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ theme:
3535
nav:
3636
- Overview: index.md
3737
- Schemas: schemas.md
38-
- Pagination: pagination.md
39-
- Routing: routing.md
4038
- Backends:
4139
- In Memory: backends/memory.md
4240
- SQLAlchemy: backends/sqlalchemy.md
4341
- Databases (async): backends/async.md
4442
- Ormar (async): backends/ormar.md
4543
- Tortoise (async): backends/tortoise.md
44+
- Routing: routing.md
45+
- Pagination: pagination.md
4646
- Dependencies: dependencies.md
4747
- Contributing: contributing.md
4848
- Releases: releases.md

fastapi_crudrouter/core/_base.py

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
from typing import Any, Callable, Generic, List, Optional, Type
1+
from typing import Any, Callable, Generic, List, Optional, Type, Union
22

33
from fastapi import APIRouter, HTTPException
44
from fastapi.types import DecoratedCallable
55

6-
from ._types import T
6+
from ._types import T, DEPENDENCIES
77
from ._utils import pagination_factory, schema_factory
88

99
NOT_FOUND = HTTPException(404, "Item not found")
@@ -23,12 +23,12 @@ def __init__(
2323
prefix: Optional[str] = None,
2424
tags: Optional[List[str]] = None,
2525
paginate: Optional[int] = None,
26-
get_all_route: bool = True,
27-
get_one_route: bool = True,
28-
create_route: bool = True,
29-
update_route: bool = True,
30-
delete_one_route: bool = True,
31-
delete_all_route: bool = True,
26+
get_all_route: Union[bool, DEPENDENCIES] = True,
27+
get_one_route: Union[bool, DEPENDENCIES] = True,
28+
create_route: Union[bool, DEPENDENCIES] = True,
29+
update_route: Union[bool, DEPENDENCIES] = True,
30+
delete_one_route: Union[bool, DEPENDENCIES] = True,
31+
delete_all_route: Union[bool, DEPENDENCIES] = True,
3232
**kwargs: Any,
3333
) -> None:
3434

@@ -53,59 +53,75 @@ def __init__(
5353
super().__init__(prefix=prefix, tags=tags, **kwargs)
5454

5555
if get_all_route:
56-
super().add_api_route(
56+
self._add_api_route(
5757
"",
5858
self._get_all(),
5959
methods=["GET"],
6060
response_model=Optional[List[self.schema]], # type: ignore
6161
summary="Get All",
62+
dependencies=get_all_route,
6263
)
6364

6465
if create_route:
65-
super().add_api_route(
66+
self._add_api_route(
6667
"",
6768
self._create(),
6869
methods=["POST"],
6970
response_model=self.schema,
7071
summary="Create One",
72+
dependencies=create_route,
7173
)
7274

7375
if delete_all_route:
74-
super().add_api_route(
76+
self._add_api_route(
7577
"",
7678
self._delete_all(),
7779
methods=["DELETE"],
7880
response_model=Optional[List[self.schema]], # type: ignore
7981
summary="Delete All",
82+
dependencies=delete_all_route,
8083
)
8184

8285
if get_one_route:
83-
super().add_api_route(
86+
self._add_api_route(
8487
"/{item_id}",
8588
self._get_one(),
8689
methods=["GET"],
8790
response_model=self.schema,
8891
summary="Get One",
92+
dependencies=get_one_route,
8993
)
9094

9195
if update_route:
92-
super().add_api_route(
96+
self._add_api_route(
9397
"/{item_id}",
9498
self._update(),
9599
methods=["PUT"],
96100
response_model=self.schema,
97101
summary="Update One",
102+
dependencies=update_route,
98103
)
99104

100105
if delete_one_route:
101-
super().add_api_route(
106+
self._add_api_route(
102107
"/{item_id}",
103108
self._delete_one(),
104109
methods=["DELETE"],
105110
response_model=self.schema,
106111
summary="Delete One",
112+
dependencies=delete_one_route,
107113
)
108114

115+
def _add_api_route(
116+
self,
117+
path: str,
118+
endpoint: Callable[..., Any],
119+
dependencies: Union[bool, DEPENDENCIES],
120+
**kwargs: Any,
121+
) -> None:
122+
dependencies = [] if isinstance(dependencies, bool) else dependencies
123+
super().add_api_route(path, endpoint, dependencies=dependencies, **kwargs)
124+
109125
def api_route(
110126
self, path: str, *args: Any, **kwargs: Any
111127
) -> Callable[[DecoratedCallable], DecoratedCallable]:

fastapi_crudrouter/core/_types.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
from typing import Dict, Optional, TypeVar
1+
from typing import Dict, TypeVar, Optional, Sequence
22

3+
from fastapi.params import Depends
34
from pydantic import BaseModel
45

56
PAGINATION = Dict[str, Optional[int]]
67
PYDANTIC_SCHEMA = BaseModel
78

89
T = TypeVar("T", bound=BaseModel)
10+
DEPENDENCIES = Optional[Sequence[Depends]]

fastapi_crudrouter/core/databases.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
from typing import Any, Callable, List, Mapping, Type, Coroutine, Optional
1+
from typing import (
2+
Any,
3+
Callable,
4+
List,
5+
Mapping,
6+
Type,
7+
Coroutine,
8+
Optional,
9+
Union,
10+
)
211

312
from fastapi import HTTPException
413

514
from . import CRUDGenerator, NOT_FOUND, _utils
6-
from ._types import PAGINATION, PYDANTIC_SCHEMA
15+
from ._types import PAGINATION, PYDANTIC_SCHEMA, DEPENDENCIES
716

817
try:
918
from sqlalchemy.sql.schema import Table
@@ -29,12 +38,12 @@ def __init__(
2938
prefix: Optional[str] = None,
3039
tags: Optional[List[str]] = None,
3140
paginate: Optional[int] = None,
32-
get_all_route: bool = True,
33-
get_one_route: bool = True,
34-
create_route: bool = True,
35-
update_route: bool = True,
36-
delete_one_route: bool = True,
37-
delete_all_route: bool = True,
41+
get_all_route: Union[bool, DEPENDENCIES] = True,
42+
get_one_route: Union[bool, DEPENDENCIES] = True,
43+
create_route: Union[bool, DEPENDENCIES] = True,
44+
update_route: Union[bool, DEPENDENCIES] = True,
45+
delete_one_route: Union[bool, DEPENDENCIES] = True,
46+
delete_all_route: Union[bool, DEPENDENCIES] = True,
3847
**kwargs: Any
3948
) -> None:
4049
assert (

fastapi_crudrouter/core/mem.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from typing import Any, Callable, List, Type, cast, Optional
1+
from typing import Any, Callable, List, Type, cast, Optional, Union
22

33
from . import CRUDGenerator, NOT_FOUND
4-
from ._types import PAGINATION, PYDANTIC_SCHEMA as SCHEMA
4+
from ._types import DEPENDENCIES, PAGINATION, PYDANTIC_SCHEMA as SCHEMA
55

66
CALLABLE = Callable[..., SCHEMA]
77
CALLABLE_LIST = Callable[..., List[SCHEMA]]
@@ -16,12 +16,12 @@ def __init__(
1616
prefix: Optional[str] = None,
1717
tags: Optional[List[str]] = None,
1818
paginate: Optional[int] = None,
19-
get_all_route: bool = True,
20-
get_one_route: bool = True,
21-
create_route: bool = True,
22-
update_route: bool = True,
23-
delete_one_route: bool = True,
24-
delete_all_route: bool = True,
19+
get_all_route: Union[bool, DEPENDENCIES] = True,
20+
get_one_route: Union[bool, DEPENDENCIES] = True,
21+
create_route: Union[bool, DEPENDENCIES] = True,
22+
update_route: Union[bool, DEPENDENCIES] = True,
23+
delete_one_route: Union[bool, DEPENDENCIES] = True,
24+
delete_all_route: Union[bool, DEPENDENCIES] = True,
2525
**kwargs: Any
2626
) -> None:
2727
super().__init__(

fastapi_crudrouter/core/ormar.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
Type,
77
cast,
88
Coroutine,
9+
Union,
910
)
1011

1112
from fastapi import HTTPException
1213

1314
from . import CRUDGenerator, NOT_FOUND, _utils
14-
from ._types import PAGINATION
15+
from ._types import DEPENDENCIES, PAGINATION
1516

1617
try:
1718
from ormar import Model, NoMatch
@@ -34,12 +35,12 @@ def __init__(
3435
prefix: Optional[str] = None,
3536
tags: Optional[List[str]] = None,
3637
paginate: Optional[int] = None,
37-
get_all_route: bool = True,
38-
get_one_route: bool = True,
39-
create_route: bool = True,
40-
update_route: bool = True,
41-
delete_one_route: bool = True,
42-
delete_all_route: bool = True,
38+
get_all_route: Union[bool, DEPENDENCIES] = True,
39+
get_one_route: Union[bool, DEPENDENCIES] = True,
40+
create_route: Union[bool, DEPENDENCIES] = True,
41+
update_route: Union[bool, DEPENDENCIES] = True,
42+
delete_one_route: Union[bool, DEPENDENCIES] = True,
43+
delete_all_route: Union[bool, DEPENDENCIES] = True,
4344
**kwargs: Any
4445
) -> None:
4546
assert ormar_installed, "Ormar must be installed to use the OrmarCRUDRouter."

fastapi_crudrouter/core/sqlalchemy.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
from typing import Any, Callable, List, Type, Generator, Optional
1+
from typing import Any, Callable, List, Type, Generator, Optional, Union
22

33
from fastapi import Depends, HTTPException
44

55
from . import CRUDGenerator, NOT_FOUND, _utils
6-
from ._types import PAGINATION, PYDANTIC_SCHEMA as SCHEMA
6+
from ._types import DEPENDENCIES, PAGINATION, PYDANTIC_SCHEMA as SCHEMA
77

88
try:
99
from sqlalchemy.orm import Session
@@ -28,12 +28,12 @@ def __init__(
2828
prefix: Optional[str] = None,
2929
tags: Optional[List[str]] = None,
3030
paginate: Optional[int] = None,
31-
get_all_route: bool = True,
32-
get_one_route: bool = True,
33-
create_route: bool = True,
34-
update_route: bool = True,
35-
delete_one_route: bool = True,
36-
delete_all_route: bool = True,
31+
get_all_route: Union[bool, DEPENDENCIES] = True,
32+
get_one_route: Union[bool, DEPENDENCIES] = True,
33+
create_route: Union[bool, DEPENDENCIES] = True,
34+
update_route: Union[bool, DEPENDENCIES] = True,
35+
delete_one_route: Union[bool, DEPENDENCIES] = True,
36+
delete_all_route: Union[bool, DEPENDENCIES] = True,
3737
**kwargs: Any
3838
) -> None:
3939
assert (

0 commit comments

Comments
 (0)