I am trying to connect to mongodb in FastAPI. I am repeatedly getting this exception.
File - main.py
app = FastAPI(
title=config.PROJECT_NAME, docs_url="/api/docs", openapi_url="/api"
)
@app.get("/api/testing")
async def testit():
user_collection = readernetwork_db.get_collection("user_collection")
all_users = await user_collection.find_one({"email": "sample_email"})
print("all users --- ", all_users)
return all_users
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", reload=True, port=8888)
File - session.py
import motor.motor_asyncio
from app.core import config
print("here we go again....")
client = motor.motor_asyncio.AsyncIOMotorClient(
config.MONGOATLAS_DATABASE_URI)
readernetwork_db = client.get_database("readernetwork")
Exception -:
all_users = await user_collection.find_one({"email": "sample_email"})
RuntimeError: Task <Task pending name='Task-4' coro=<RequestResponseCycle.run_asgi() running at /usr/local/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py:389> cb=[set.discard()]> got Future <Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/local/lib/python3.8/asyncio/futures.py:360]> attached to a different loop
I don't know where I am getting this wrong. Should I specify a event loop to motor?
Without the need for using global... You can access the application state from the request given to any route.
#main.py
async def open_db() -> AsyncIOMotorClient:
app.state.mongodb = AsyncIOMotorClient(DB_URL)
async def close_db():
app.state.mongodb.close()
app.add_event_handler('startup', open_db)
app.add_event_handler('shutdown', close_db)
Within each request to a given route you will have access to the app state. For example,
@app.route('/{username}')
async def index(request: Request, username: str):
user = await request.app.state.mongodb['auth']['users'].find_one({"username" : username})
You can even make it easier by doing something like this in your open_db function. Specify a state value like 'users' to be a specific Collections instance.
async def open_db() -> AsyncIOMotorClient:
app.state.mongodb = AsyncIOMotorClient(DB_URL)
app.state.users = app.state.mongodb['auth']['users']
Now you can just do this,
@app.route('/{username}')
async def index(request: Request, username: str):
user = await request.app.state.users.find_one({"username" : username})
You can have mongodb motor
client in the global scope, but creating and closing it should be done inside an async function. The most preferable way of doing that in startup
and shutdown
handler of the application. Like so:
# mongodb.py
from motor.motor_asyncio import AsyncIOMotorClient
db_client: AsyncIOMotorClient = None
async def get_db_client() -> AsyncIOMotorClient:
"""Return database client instance."""
return db_client
async def connect_db():
"""Create database connection."""
global db_client
db_client = AsyncIOMotorClient(DB_URL)
async def close_db():
"""Close database connection."""
db_client.close()
# main.py
app = FastAPI(title=PROJECT_NAME)
...
app.add_event_handler("startup", connect_db)
app.add_event_handler("shutdown", close_db)
Note that you need the line global db_client
to modify the global variable defined beforehand.
client = AsyncIOMotorClient()
client.get_io_loop = asyncio.get_event_loop
works for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With