mirror of https://github.com/tiangolo/fastapi.git
✨ Add support for tags with Enums (#4468)
This commit is contained in:
parent
0f8349fcb7
commit
569afb4378
|
|
@ -64,6 +64,18 @@ They will be added to the OpenAPI schema and used by the automatic documentation
|
||||||
|
|
||||||
<img src="/img/tutorial/path-operation-configuration/image01.png">
|
<img src="/img/tutorial/path-operation-configuration/image01.png">
|
||||||
|
|
||||||
|
### Tags with Enums
|
||||||
|
|
||||||
|
If you have a big application, you might end up accumulating **several tags**, and you would want to make sure you always use the **same tag** for related *path operations*.
|
||||||
|
|
||||||
|
In these cases, it could make sense to store the tags in an `Enum`.
|
||||||
|
|
||||||
|
**FastAPI** supports that the same way as with plain strings:
|
||||||
|
|
||||||
|
```Python hl_lines="1 8-10 13 18"
|
||||||
|
{!../../../docs_src/path_operation_configuration/tutorial002b.py!}
|
||||||
|
```
|
||||||
|
|
||||||
## Summary and description
|
## Summary and description
|
||||||
|
|
||||||
You can add a `summary` and `description`:
|
You can add a `summary` and `description`:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class Tags(Enum):
|
||||||
|
items = "items"
|
||||||
|
users = "users"
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/items/", tags=[Tags.items])
|
||||||
|
async def get_items():
|
||||||
|
return ["Portal gun", "Plumbus"]
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/users/", tags=[Tags.users])
|
||||||
|
async def read_users():
|
||||||
|
return ["Rick", "Morty"]
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
from enum import Enum
|
||||||
from typing import Any, Callable, Coroutine, Dict, List, Optional, Sequence, Type, Union
|
from typing import Any, Callable, Coroutine, Dict, List, Optional, Sequence, Type, Union
|
||||||
|
|
||||||
from fastapi import routing
|
from fastapi import routing
|
||||||
|
|
@ -219,7 +220,7 @@ class FastAPI(Starlette):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[Depends]] = None,
|
dependencies: Optional[Sequence[Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -273,7 +274,7 @@ class FastAPI(Starlette):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[Depends]] = None,
|
dependencies: Optional[Sequence[Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -342,7 +343,7 @@ class FastAPI(Starlette):
|
||||||
router: routing.APIRouter,
|
router: routing.APIRouter,
|
||||||
*,
|
*,
|
||||||
prefix: str = "",
|
prefix: str = "",
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[Depends]] = None,
|
dependencies: Optional[Sequence[Depends]] = None,
|
||||||
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
||||||
deprecated: Optional[bool] = None,
|
deprecated: Optional[bool] = None,
|
||||||
|
|
@ -368,7 +369,7 @@ class FastAPI(Starlette):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[Depends]] = None,
|
dependencies: Optional[Sequence[Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -419,7 +420,7 @@ class FastAPI(Starlette):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[Depends]] = None,
|
dependencies: Optional[Sequence[Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -470,7 +471,7 @@ class FastAPI(Starlette):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[Depends]] = None,
|
dependencies: Optional[Sequence[Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -521,7 +522,7 @@ class FastAPI(Starlette):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[Depends]] = None,
|
dependencies: Optional[Sequence[Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -572,7 +573,7 @@ class FastAPI(Starlette):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[Depends]] = None,
|
dependencies: Optional[Sequence[Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -623,7 +624,7 @@ class FastAPI(Starlette):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[Depends]] = None,
|
dependencies: Optional[Sequence[Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -674,7 +675,7 @@ class FastAPI(Starlette):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[Depends]] = None,
|
dependencies: Optional[Sequence[Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -725,7 +726,7 @@ class FastAPI(Starlette):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[Depends]] = None,
|
dependencies: Optional[Sequence[Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import email.message
|
import email.message
|
||||||
import enum
|
|
||||||
import inspect
|
import inspect
|
||||||
import json
|
import json
|
||||||
|
from enum import Enum, IntEnum
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
Callable,
|
Callable,
|
||||||
|
|
@ -305,7 +305,7 @@ class APIRoute(routing.Route):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -330,7 +330,7 @@ class APIRoute(routing.Route):
|
||||||
openapi_extra: Optional[Dict[str, Any]] = None,
|
openapi_extra: Optional[Dict[str, Any]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
# normalise enums e.g. http.HTTPStatus
|
# normalise enums e.g. http.HTTPStatus
|
||||||
if isinstance(status_code, enum.IntEnum):
|
if isinstance(status_code, IntEnum):
|
||||||
status_code = int(status_code)
|
status_code = int(status_code)
|
||||||
self.path = path
|
self.path = path
|
||||||
self.endpoint = endpoint
|
self.endpoint = endpoint
|
||||||
|
|
@ -438,7 +438,7 @@ class APIRouter(routing.Router):
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
prefix: str = "",
|
prefix: str = "",
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
default_response_class: Type[Response] = Default(JSONResponse),
|
default_response_class: Type[Response] = Default(JSONResponse),
|
||||||
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
||||||
|
|
@ -466,7 +466,7 @@ class APIRouter(routing.Router):
|
||||||
"/"
|
"/"
|
||||||
), "A path prefix must not end with '/', as the routes will start with '/'"
|
), "A path prefix must not end with '/', as the routes will start with '/'"
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
self.tags: List[str] = tags or []
|
self.tags: List[Union[str, Enum]] = tags or []
|
||||||
self.dependencies = list(dependencies or []) or []
|
self.dependencies = list(dependencies or []) or []
|
||||||
self.deprecated = deprecated
|
self.deprecated = deprecated
|
||||||
self.include_in_schema = include_in_schema
|
self.include_in_schema = include_in_schema
|
||||||
|
|
@ -483,7 +483,7 @@ class APIRouter(routing.Router):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -557,7 +557,7 @@ class APIRouter(routing.Router):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -634,7 +634,7 @@ class APIRouter(routing.Router):
|
||||||
router: "APIRouter",
|
router: "APIRouter",
|
||||||
*,
|
*,
|
||||||
prefix: str = "",
|
prefix: str = "",
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
default_response_class: Type[Response] = Default(JSONResponse),
|
default_response_class: Type[Response] = Default(JSONResponse),
|
||||||
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
||||||
|
|
@ -738,7 +738,7 @@ class APIRouter(routing.Router):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -790,7 +790,7 @@ class APIRouter(routing.Router):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -842,7 +842,7 @@ class APIRouter(routing.Router):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -894,7 +894,7 @@ class APIRouter(routing.Router):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -946,7 +946,7 @@ class APIRouter(routing.Router):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -998,7 +998,7 @@ class APIRouter(routing.Router):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -1050,7 +1050,7 @@ class APIRouter(routing.Router):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
@ -1102,7 +1102,7 @@ class APIRouter(routing.Router):
|
||||||
*,
|
*,
|
||||||
response_model: Optional[Type[Any]] = None,
|
response_model: Optional[Type[Any]] = None,
|
||||||
status_code: Optional[int] = None,
|
status_code: Optional[int] = None,
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[Union[str, Enum]]] = None,
|
||||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||||
summary: Optional[str] = None,
|
summary: Optional[str] = None,
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from docs_src.path_operation_configuration.tutorial002b import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
openapi_schema = {
|
||||||
|
"openapi": "3.0.2",
|
||||||
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
|
"paths": {
|
||||||
|
"/items/": {
|
||||||
|
"get": {
|
||||||
|
"tags": ["items"],
|
||||||
|
"summary": "Get Items",
|
||||||
|
"operationId": "get_items_items__get",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/users/": {
|
||||||
|
"get": {
|
||||||
|
"tags": ["users"],
|
||||||
|
"summary": "Read Users",
|
||||||
|
"operationId": "read_users_users__get",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_openapi_schema():
|
||||||
|
response = client.get("/openapi.json")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == openapi_schema
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_items():
|
||||||
|
response = client.get("/items/")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == ["Portal gun", "Plumbus"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_users():
|
||||||
|
response = client.get("/users/")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == ["Rick", "Morty"]
|
||||||
Loading…
Reference in New Issue