mfreeborn / fastapi-sqlalchemy

Adds simple SQLAlchemy support to FastAPI
MIT License
594 stars 34 forks source link

How does the library use asynchronous mode? #32

Open pylixm opened 3 years ago

pylixm commented 3 years ago

How does the library use asynchronous mode?

Below is the code I tried, but it not work.

from fastapi import FastAPI
from fastapi_sqlalchemy import DBSessionMiddleware  # middleware helper
from fastapi_sqlalchemy import db  # an object to provide global access to a database session

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession

from models.user import User

app = FastAPI()

app.add_middleware(DBSessionMiddleware, db_url="sqlite://", 
                   custom_engine=create_async_engine,
                   session_args={
                        'expire_on_commit': False,
                        'class_': AsyncSession
                   })

# once the middleware is applied, any route can then access the database session
# from the global ``db``

@app.get("/users")
async def get_users():
    users = await db.session.query(User).all()

    return users

if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app=app, host="127.0.0.1", port=8001, log_level="debug")

Does anyone else have any ideas?

GeekTan commented 3 years ago

例如用postgresql 需要配合异步驱动才行,参考sqlalchemy手册的asyncio support

cwjayroe commented 2 years ago

I think that if you are creating your own engine, you will need to give the middleware the actual async engine object rather than the function to create the engine. Maybe this code will help you (I havent run it but hopefully it'll point you in the right direction).

from fastapi import FastAPI
from fastapi_sqlalchemy import DBSessionMiddleware  # middleware helper
from fastapi_sqlalchemy import db  # an object to provide global access to a database session
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from models.user import User

app = FastAPI()

# async engine object to give to the middleware
engine = create_async_engine('sqlite://')

app.add_middleware(DBSessionMiddleware, 
                   custom_engine=engine,
                   session_args={
                        'expire_on_commit': False,
                        'class_': AsyncSession
                   })

# once the middleware is applied, any route can then access the database session
# from the global ``db``

@app.get("/users")
async def get_users():
    users = await db.session.query(User).all()

    return users

if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app=app, host="127.0.0.1", port=8001, log_level="debug")
cancan101 commented 2 years ago

Is that enough or does this library also need to add asynchronous context manager support to DBSession? For example:

class DBSession(metaclass=DBSessionMeta):
    def __init__(self, session_args: Dict = None, commit_on_exit: bool = False):
        self.token = None
        self.session_args = session_args or {}
        self.commit_on_exit = commit_on_exit

    async def __aenter__(self):
        if not isinstance(_Session, sessionmaker):
            raise SessionNotInitialisedError
        self.token = _session.set(_Session(**self.session_args))
        return type(self)

    async def __aexit__(self, exc_type, exc_value, traceback):
        sess = _session.get()
        if exc_type is not None:
            await sess.rollback()

        if self.commit_on_exit:
            await sess.commit()

        await sess.close()
        _session.reset(self.token)

and then tweak the dispatch in the middleware:

    async def dispatch(self, request: Request, call_next: RequestResponseEndpoint):
        async with db(commit_on_exit=self.commit_on_exit):
            response = await call_next(request)
        return response
h0rn3t commented 2 years ago

@pylixm https://github.com/h0rn3t/fastapi-async-sqlalchemy u can try this

hadrien commented 10 months ago

@pylixm I'm late to the party: Check https://github.com/dialoguemd/fastapi-sqla/ as well. It's compatible with sqla 1.3, 1.4, 2.0 and with async support.