I have a problem using FastAPI, trying to dynamically define routes. It seems that the final route/handler defined overrides all the previous ones (see image below)
Situation: I have a lot of Pydantic models as a list (imported from models.py
: simplified in example), and would like to create a GET endpoint for each model.
Something like this (my app is more complex — there is a reason it is structured as it is — but I've simplified it down to this, in order to find the problem, which still occurs):
# models.py
class Thing(BaseNode):
value: str
class Other(BaseNode):
value: str
model_list = [Thing, Other]
# app.py
from models import model_list
app = FastApi()
# Iterate over the models list and define a get function
for model in models_list:
@app.get(f"/{model.__name__.lower()}")
def get() -> str:
print(model.__name__)
return f"Getting {model.__name__)
I'm obviously doing something wrong (or FastAPI references the handler function in some weird way... by module/function name(?) so the get() function is being re-defined for each iteration?) Any ideas, or better way to structure this? (I can't manually write out a new function for each model!)
Thanks!
UPDATE: Update:
If I define the get()
function inside another function, it works:
for model in models_list:
def get(model):
def _get():
print("Getting", model)
return f"Getting {model.__name__}"
return _get
app.get(.get(f"/{model.__name__.lower()}", name=f"{model.__name__}.View")(get(model))
Most likely the issue lies in the fact that you are using the same model
reference for every endpoint created, and hence, it is always assigned the last value given in the for loop.
You should rather use a helper function that returns an inner function, as demonstrated in Option 2 of this answer.
from fastapi import FastAPI, APIRouter, Request
from pydantic import BaseModel
class One(BaseModel):
value: str
class Two(BaseModel):
value: str
app = FastAPI()
models = [One, Two]
def create_endpoint(m_name: str):
async def endpoint(request: Request):
print(request.url)
return f"You called {m_name}"
return endpoint
for m in models:
app.add_api_route(f"/{m.__name__.lower()}", create_endpoint(m.__name__), methods=["GET"])