Я пытаюсь инициализировать ConnectionPool, используя asyncpg, а затем использую этот пул для создания соединения каждый раз, когда мне нужно прочитать что-то из моей БД. Все работало нормально, пока я не переместил конечные точки SQL на APIRouter. Я немного растерялся, так как не смог найти документов для этого конкретного случая.
Чтобы создать соединение, я использую жизненный цикл приложения main.py:
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.db_pool = await create_async_pool()
yield
await app.state.db_pool.close()
Затем я объявляю две зависимости в main.py:
async def get_database():
yield app.state.db_pool
async def get_db_connection(pool: asyncpg.pool.Pool = Depends(get_database)):
async with pool.acquire() as connection:
yield connection
До сих пор все работает отлично, и я могу использовать свою базу данных на конечных точках своего приложения, написанных на main.py. Теперь я хочу перенести эти конечные точки на новый маршрутизатор API в файле с именем analytics.py. Импорт этих зависимостей непосредственно из моего analytics.py возвращает ошибки циклического импорта, но я совершенно не могу передать последнюю зависимость в свой новый маршрутизатор Analytics. Мое дерево файлов сейчас выглядит так:
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── analytics
│ ├── __init__.py
│ └── analytics.py
Есть идеи, как я могу это исправить? Определенно должно быть что-то, чего мне не хватает
Мои полные файлы выглядят так: main.py:
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.db_pool = await create_async_pool()
yield
await app.state.db_pool.close()
app = FastAPI(lifespan=lifespan)
app.include_router(analytics.router)
async def get_database():
yield app.state.db_pool
async def get_db_connection(pool: asyncpg.pool.Pool = Depends(get_database)):
async with pool.acquire() as connection:
yield connection
@app.get("/"):
async def hello():
return {"message": "Hello world!"}
analytics.py:
from fastapi import APIRouter
from app.main import get_db_connection
router = APIRouter(
prefix = "/analytics"
)
@router.post("/test")
async def test_analytics(input_data, db = Depends(get_db_connection)):
return "OK"
Ошибка в этой конфигурации:
Import Error: cannot import name 'get_db_connection' from partially initialized module 'app.main' (most likely due to a circular import)
добавьте код ваших модулей. По крайней мере их разделы импорта. Также добавьте обратную трассировку ошибок
эй @YuriiMotov, я обновил свой ответ, включив в него код и обратную трассировку, спасибо!






Мы не видим полный код ваших модулей main и analytics. Итак, трудно точно сказать, почему у вас возникают ошибки циклического импорта.
Но перемещение этих двух зависимостей в отдельный файл (например, dependencies.py) должно вам помочь.
Также измените lifespant, чтобы:
@asynccontextmanager
async def lifespan(app: FastAPI):
db_pool = await create_async_pool()
yield {"db_pool": db_pool}
await db_pool.close()
И используйте request.state вместо app.state в get_database:
from fastapi import Request
async def get_database(request: Request):
yield request.state.db_pool
Файл analytics.py содержит только базовый маршрутизатор API FastAPI с некоторыми конечными точками, которые получают упомянутое соединение с базой данных в качестве зависимости. Перенос обеих функций в отдельный файл начал выдавать ошибки о том, что app.state не найден 🤔
Используйте request.state вместо app.state. Я обновил ответ
перемещение зависимостей в отдельный файл не помогает. При использовании request.state возвращается ошибка: Attribute error: 'state' object has no attribute 'db_pool'. Я отредактировал свой ответ, включив в него остальную часть кода.
Я забыл еще одну вещь: вы должны получить словарь с помощью db_pool из lifespan вместо установки атрибута app.state. Изменил ответ
Спасибо, Юрий, все работает! Однако мне только что пришло в голову, возможно ли или лучше просто объявить зависимость от срока службы API-маршрутизатора, а не от срока службы приложения? Мне больше нигде не нужна зависимость от базы данных 🤔
Я думаю, это не имеет значения. Вы можете перенести его на срок службы маршрутизатора. Но оставьте функции зависимостей БД в отдельном файле. Кстати, справедливо ли, что общее количество голосов равно «-1» за правильный ответ?
Я не думаю, что это справедливо, нет. Я отметил это как правильный ответ, надеюсь, кто-нибудь удалит свой отрицательный голос 😁
Некоторое время назад я ответил на аналогичный вопрос здесь: stackoverflow.com/a/78596305/21152416. Думаю, это может быть полезно и вам!