mirror of
https://github.com/fastapi/fastapi.git
synced 2026-09-14 13:36:21 +08:00
✨ Add parameter dependencies to path operation decorators and include_router (#235)
* ✨ Implement dependencies in decorator and .include_router * 📝 Add docs for parameter dependencies * ✅ Add tests for dependencies parameter * 🔥 Remove debugging prints in tests * 📝 Update release notes
This commit is contained in:
committed by
GitHub
parent
7c50025c47
commit
e92b43b5c8
+27
-1
@@ -3,6 +3,7 @@ from typing import Any, Callable, Dict, List, Optional, Type, Union
|
||||
from fastapi import routing
|
||||
from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
|
||||
from fastapi.openapi.utils import get_openapi
|
||||
from fastapi.params import Depends
|
||||
from pydantic import BaseModel
|
||||
from starlette.applications import Starlette
|
||||
from starlette.exceptions import ExceptionMiddleware, HTTPException
|
||||
@@ -111,6 +112,7 @@ class FastAPI(Starlette):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -128,6 +130,7 @@ class FastAPI(Starlette):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -147,6 +150,7 @@ class FastAPI(Starlette):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -165,6 +169,7 @@ class FastAPI(Starlette):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -186,10 +191,15 @@ class FastAPI(Starlette):
|
||||
*,
|
||||
prefix: str = "",
|
||||
tags: List[str] = None,
|
||||
dependencies: List[Depends] = None,
|
||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||
) -> None:
|
||||
self.router.include_router(
|
||||
router, prefix=prefix, tags=tags, responses=responses or {}
|
||||
router,
|
||||
prefix=prefix,
|
||||
tags=tags,
|
||||
dependencies=dependencies,
|
||||
responses=responses or {},
|
||||
)
|
||||
|
||||
def get(
|
||||
@@ -199,6 +209,7 @@ class FastAPI(Starlette):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -214,6 +225,7 @@ class FastAPI(Starlette):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -232,6 +244,7 @@ class FastAPI(Starlette):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -247,6 +260,7 @@ class FastAPI(Starlette):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -265,6 +279,7 @@ class FastAPI(Starlette):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -280,6 +295,7 @@ class FastAPI(Starlette):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -298,6 +314,7 @@ class FastAPI(Starlette):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -313,6 +330,7 @@ class FastAPI(Starlette):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -331,6 +349,7 @@ class FastAPI(Starlette):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -346,6 +365,7 @@ class FastAPI(Starlette):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -364,6 +384,7 @@ class FastAPI(Starlette):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -379,6 +400,7 @@ class FastAPI(Starlette):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -397,6 +419,7 @@ class FastAPI(Starlette):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -412,6 +435,7 @@ class FastAPI(Starlette):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -430,6 +454,7 @@ class FastAPI(Starlette):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -445,6 +470,7 @@ class FastAPI(Starlette):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
|
||||
@@ -52,7 +52,7 @@ sequence_types = (list, set, tuple)
|
||||
sequence_shape_to_type = {Shape.LIST: list, Shape.SET: set, Shape.TUPLE: tuple}
|
||||
|
||||
|
||||
def get_sub_dependant(
|
||||
def get_param_sub_dependant(
|
||||
*, param: inspect.Parameter, path: str, security_scopes: List[str] = None
|
||||
) -> Dependant:
|
||||
depends: params.Depends = param.default
|
||||
@@ -60,6 +60,30 @@ def get_sub_dependant(
|
||||
dependency = depends.dependency
|
||||
else:
|
||||
dependency = param.annotation
|
||||
return get_sub_dependant(
|
||||
depends=depends,
|
||||
dependency=dependency,
|
||||
path=path,
|
||||
name=param.name,
|
||||
security_scopes=security_scopes,
|
||||
)
|
||||
|
||||
|
||||
def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> Dependant:
|
||||
assert callable(
|
||||
depends.dependency
|
||||
), "A parameter-less dependency must have a callable dependency"
|
||||
return get_sub_dependant(depends=depends, dependency=depends.dependency, path=path)
|
||||
|
||||
|
||||
def get_sub_dependant(
|
||||
*,
|
||||
depends: params.Depends,
|
||||
dependency: Callable,
|
||||
path: str,
|
||||
name: str = None,
|
||||
security_scopes: List[str] = None,
|
||||
) -> Dependant:
|
||||
security_requirement = None
|
||||
security_scopes = security_scopes or []
|
||||
if isinstance(depends, params.Security):
|
||||
@@ -73,7 +97,7 @@ def get_sub_dependant(
|
||||
security_scheme=dependency, scopes=use_scopes
|
||||
)
|
||||
sub_dependant = get_dependant(
|
||||
path=path, call=dependency, name=param.name, security_scopes=security_scopes
|
||||
path=path, call=dependency, name=name, security_scopes=security_scopes
|
||||
)
|
||||
if security_requirement:
|
||||
sub_dependant.security_requirements.append(security_requirement)
|
||||
@@ -111,7 +135,7 @@ def get_dependant(
|
||||
for param_name in signature_params:
|
||||
param = signature_params[param_name]
|
||||
if isinstance(param.default, params.Depends):
|
||||
sub_dependant = get_sub_dependant(
|
||||
sub_dependant = get_param_sub_dependant(
|
||||
param=param, path=path, security_scopes=security_scopes
|
||||
)
|
||||
dependant.dependencies.append(sub_dependant)
|
||||
@@ -277,8 +301,8 @@ async def solve_dependencies(
|
||||
solved = await sub_dependant.call(**sub_values)
|
||||
else:
|
||||
solved = await run_in_threadpool(sub_dependant.call, **sub_values)
|
||||
assert sub_dependant.name is not None, "Subdependants always have a name"
|
||||
values[sub_dependant.name] = solved
|
||||
if sub_dependant.name is not None:
|
||||
values[sub_dependant.name] = solved
|
||||
path_values, path_errors = request_params_to_args(
|
||||
dependant.path_params, request.path_params
|
||||
)
|
||||
|
||||
+34
-1
@@ -5,7 +5,12 @@ from typing import Any, Callable, Dict, List, Optional, Type, Union
|
||||
|
||||
from fastapi import params
|
||||
from fastapi.dependencies.models import Dependant
|
||||
from fastapi.dependencies.utils import get_body_field, get_dependant, solve_dependencies
|
||||
from fastapi.dependencies.utils import (
|
||||
get_body_field,
|
||||
get_dependant,
|
||||
get_parameterless_sub_dependant,
|
||||
solve_dependencies,
|
||||
)
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseConfig, BaseModel, Schema
|
||||
from pydantic.error_wrappers import ErrorWrapper, ValidationError
|
||||
@@ -101,6 +106,7 @@ class APIRoute(routing.Route):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[params.Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -135,6 +141,7 @@ class APIRoute(routing.Route):
|
||||
self.response_field = None
|
||||
self.status_code = status_code
|
||||
self.tags = tags or []
|
||||
self.dependencies = dependencies or []
|
||||
self.summary = summary
|
||||
self.description = description or inspect.cleandoc(self.endpoint.__doc__ or "")
|
||||
self.response_description = response_description
|
||||
@@ -175,6 +182,10 @@ class APIRoute(routing.Route):
|
||||
endpoint
|
||||
), f"An endpoint must be a function or method"
|
||||
self.dependant = get_dependant(path=path, call=self.endpoint)
|
||||
for depends in self.dependencies[::-1]:
|
||||
self.dependant.dependencies.insert(
|
||||
0, get_parameterless_sub_dependant(depends=depends, path=path)
|
||||
)
|
||||
self.body_field = get_body_field(dependant=self.dependant, name=self.name)
|
||||
self.app = request_response(
|
||||
get_app(
|
||||
@@ -196,6 +207,7 @@ class APIRouter(routing.Router):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[params.Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -213,6 +225,7 @@ class APIRouter(routing.Router):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -233,6 +246,7 @@ class APIRouter(routing.Router):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[params.Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -251,6 +265,7 @@ class APIRouter(routing.Router):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -272,6 +287,7 @@ class APIRouter(routing.Router):
|
||||
*,
|
||||
prefix: str = "",
|
||||
tags: List[str] = None,
|
||||
dependencies: List[params.Depends] = None,
|
||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||
) -> None:
|
||||
if prefix:
|
||||
@@ -290,6 +306,7 @@ class APIRouter(routing.Router):
|
||||
response_model=route.response_model,
|
||||
status_code=route.status_code,
|
||||
tags=(route.tags or []) + (tags or []),
|
||||
dependencies=(dependencies or []) + (route.dependencies or []),
|
||||
summary=route.summary,
|
||||
description=route.description,
|
||||
response_description=route.response_description,
|
||||
@@ -321,6 +338,7 @@ class APIRouter(routing.Router):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[params.Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -336,6 +354,7 @@ class APIRouter(routing.Router):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -355,6 +374,7 @@ class APIRouter(routing.Router):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[params.Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -370,6 +390,7 @@ class APIRouter(routing.Router):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -389,6 +410,7 @@ class APIRouter(routing.Router):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[params.Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -404,6 +426,7 @@ class APIRouter(routing.Router):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -423,6 +446,7 @@ class APIRouter(routing.Router):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[params.Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -438,6 +462,7 @@ class APIRouter(routing.Router):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -457,6 +482,7 @@ class APIRouter(routing.Router):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[params.Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -472,6 +498,7 @@ class APIRouter(routing.Router):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -491,6 +518,7 @@ class APIRouter(routing.Router):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[params.Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -506,6 +534,7 @@ class APIRouter(routing.Router):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -525,6 +554,7 @@ class APIRouter(routing.Router):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[params.Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -540,6 +570,7 @@ class APIRouter(routing.Router):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
@@ -559,6 +590,7 @@ class APIRouter(routing.Router):
|
||||
response_model: Type[BaseModel] = None,
|
||||
status_code: int = 200,
|
||||
tags: List[str] = None,
|
||||
dependencies: List[params.Depends] = None,
|
||||
summary: str = None,
|
||||
description: str = None,
|
||||
response_description: str = "Successful Response",
|
||||
@@ -574,6 +606,7 @@ class APIRouter(routing.Router):
|
||||
response_model=response_model,
|
||||
status_code=status_code,
|
||||
tags=tags or [],
|
||||
dependencies=dependencies or [],
|
||||
summary=summary,
|
||||
description=description,
|
||||
response_description=response_description,
|
||||
|
||||
Reference in New Issue
Block a user