ibi0tux
ibi0tux

Reputation: 2619

FastAPI dynamic multiple path parameters

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 :

Is there any way to achieve this ?

Upvotes: 5

Views: 6799

Answers (2)

pjmv
pjmv

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 : API Schema

Upvotes: 11

Grysik
Grysik

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

Related Questions