Fastapi How to access attributes of class based dependency inside route when Dependency included in the router?

Viewed 1898

I have a class based Dependency that connects to the database, and returns a session, engine, and database, so I can include those dependencies in all routes in my app. Everything works, except that I can't access the properties of the class inside the route. I can access them when I specifically set the Dependency at the route level, but since I want this injected into every route, I would like to include it at the router level. How do I do this?

Some code, to illustrate:

Dependency:

class DatabaseSession:
    engine = None
    session = None
    db = None
    def __init__(self, x_code: str = Header(None)):
        if x_code:
            self.engine, self.session, self.db = get_database_objects(x_code)
        else:
            raise HTTPException(status_code=400, detail="No code submitted")

Main app

app = FastAPI()
app.include_router(users.router, dependencies=[Depends(DatabaseSession)])

Example route, and what I'd like to accomplish

@router.get("/users/", tags=["users"], response_model=List[UserBase])
async def retrieve_all_users():
    async with db as database:
        # get all the users
        users = session.query(User).all()
        return users

The problem is, db, nor session, are defined now in the route, since I'm injecting the dependency at the router level. If I do it as shown below, it works fine:

@router.get("/users/", tags=["users"], response_model=List[UserBase])
async def retrieve_all_users(db_settings: DatabaseSession = Depends(DatabaseSession)):
    async with db_settings.db as database:
        # get all the users
        users = db_settings.session.query(User).all()
        return users

How do I get access to db and session inside each route while injecting the dependency at the router level? Thanks!

2 Answers

I've had a similar question in the past. See

FastAPI get user ID from API key

Basically, save the dependency response/values in the request object and access it from within the endpoint(s).

Here the documentation:

https://www.starlette.io/requests/

Here the sample code from the answer given to me in the question above.

async def oauth2_scheme(request: Request):
    request.state.user_id = "foo"

my_router  = APIRouter()

@my_router .get("/")
async def hello(request: Request):
    print(request.state.user_id)

app.include_router(
    my_router,
    dependencies=[Depends(oauth2_scheme)]
)

You can achieve that with a Class-based dependency too. The request object can be accesses in the __call__ method.

from fastapi import FastAPI, Request, Depends

class Simple_dep:
    def __call__(self, request: Request):
        request.state.test = 'test'

app = FastAPI(
    dependencies=[Depends(Simple_dep())]
)

@app.get("/test")
async def root(request: Request):
    return {"test": request.state.test}

Just make sure you are using Depends(Simple_dep()) instead of Depends(Simple_dep)

Related