Reputation: 2619
I would like to achieve the following :
@app.get("/{domain}/entity/{entity}/{id}")
async def read_users(domain: Domain, entity: Entity, id: Int):
pass
considering Entity
would be an Enum that could change following the selected domain.
For instance, if the domain is "architecture", Entity could be defined like :
class Entity(str, Enum):
building = "building"
floor = "floor"
but if the selected domain is "vehicle", the matching Entity would be :
class Entity(str, Enum):
engine = "engine"
wheels = "wheels"
More generally, I guess what I'm looking for is a way to make a path parameter validation dependent on another path parameter.
This way :
GET /architecture/entity/floor/1
is valid since, floor
is a valid entity for domain architecture
GET /vehicle/entity/wheels/5
is valid since, wheels
is a valid entity for domain vehicle
GET /architecture/entity/engine/1
is invalid since, engine
is not a valid entity for domain architecture
Is there any way to achieve this ?
Upvotes: 5
Views: 6799
Reputation: 555
You can use closures. The following code does not use Enums for brevity :
from fastapi import FastAPI
app = FastAPI()
domains = {"architecture": ["building","floor"], "vehicle": ["engine","wheels"]}
def set_route(domain,entity):
url = "/{}/entity/{}/{{id}}".format(domain,entity)
@app.get(url)
async def read_users(id: int):
return(f"Users of the {domain} {entity} #{id}")
for domain, entities in domains.items():
for entity in entities:
set_route(domain,entity)
And it yields the desired API schema :
Upvotes: 11
Reputation: 846
I'm not sure if exactly what you want is possible, for sure you can write controller where you add pydantic validation, and handle exception if it throws validation error:
from pydantic import BaseModel, ValidationError
from enum import Enumfrom fastapi import FastAPI, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from typing import Union, Literal
class ArchitectureEntity(BaseModel):
entity: Union[Literal['building'], Literal['floor']]
class VehicleEntity(BaseModel):
entity: Union[Literal['wheels'], Literal['engine']]
@app.exception_handler(ValidationError)
async def validation_exception_handler(request: Request, exc: ValidationError):
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content=jsonable_encoder({"detail": exc.errors(), "Error": "Entity not permitted"}),
)
@app.get("/{domain}/entity/{entity}/{id}")
async def read_users(domain: Domain, entity: Entity, id: int):
if domain == 'architecture':
entity = ArchitectureEntity(entity=entity)
elif domain == 'vehicle':
entity = VehicleEntity(entity=entity)
return {'architecture': entity}
However openapi docs will not show, that e.g architecture and engine are not allowed together.
Upvotes: 1