使用Beanie+FastAPI时fetch_links=True触发AttributeError问题求助
问题:Beanie中使用
fetch_links=True触发AttributeError: get_motor_collection 问题场景
结合FastAPI与Beanie定义以下模型:
class Project(Document): id: PydanticObjectId = Field(default_factory=PydanticObjectId) items: list[Link[ItemDB]] | None = None class ItemDB(Document): creator: Indexed(str)
编写接口通过ID获取Project并预取关联的items:
@router.get('/{project_id}', status_code=status.HTTP_200_OK, summary='Get a project', response_model_exclude_none=True) async def get_project(project_id: str) -> Project: project = await Project.get(project_id, fetch_links=True) if not project: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) return project
运行时触发错误,完整报错栈:
ERROR: Exception in ASGI application Traceback (most recent call last): File "my-project/.venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgi result = await app( # type: ignore[func-returns-value] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "my-project/.venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__ return await self.app(scope, receive, send) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "my-project/.venv/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__ await super().__call__(scope, receive, send) File "my-project/.venv/lib/python3.12/site-packages/starlette/applications.py", line 113, in __call__ await self.middleware_stack(scope, receive, send) File "my-project/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 187, in __call__ raise exc File "my-project/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 165, in __call__ await self.app(scope, receive, _send) File "my-project/.venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__ await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) File "my-project/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 62, in wrapped_app raise exc File "my-project/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 51, in wrapped_app await app(scope, receive, sender) File "my-project/.venv/lib/python3.12/site-packages/starlette/routing.py", line 715, in __call__ await self.middleware_stack(scope, receive, send) File "my-project/.venv/lib/python3.12/site-packages/starlette/routing.py", line 735, in app await route.handle(scope, receive, send) File "my-project/.venv/lib/python3.12/site-packages/starlette/routing.py", line 288, in handle await self.app(scope, receive, send) File "my-project/.venv/lib/python3.12/site-packages/starlette/routing.py", line 76, in app await wrap_app_handling_exceptions(app, request)(scope, receive, send) File "my-project/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 62, in wrapped_app raise exc File "my-project/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 51, in wrapped_app await app(scope, receive, sender) File "my-project/.venv/lib/python3.12/site-packages/starlette/routing.py", line 73, in app response = await f(request) ^^^^^^^^^^^^^^^^ File "my-project/.venv/lib/python3.12/site-packages/fastapi/routing.py", line 301, in app raw_response = await run_endpoint_function( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "my-project/.venv/lib/python3.12/site-packages/fastapi/routing.py", line 212, in run_endpoint_function return await dependant.call(**values) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "my-project/app/routers/project.py", line 46, in get_project project = await Project.get(project_id, fetch_links=True) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "my-project/.venv/lib/python3.12/site-packages/beanie/odm/documents.py", line 276, in get return await cls.find_one( ^^^^^^^^^^^^^^^^^^^ File "my-project/.venv/lib/python3.12/site-packages/beanie/odm/queries/find.py", line 1024, in __await__ document = yield from self._find_one().__await__() # type: ignore ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "my-project/.venv/lib/python3.12/site-packages/beanie/odm/queries/find.py", line 982, in _find_one return await self.document_model.find_many( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "my-project/.venv/lib/python3.12/site-packages/beanie/odm/queries/find.py", line 689, in first_or_none res = await self.limit(1).to_list() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "my-project/.venv/lib/python3.12/site-packages/beanie/odm/queries/cursor.py", line 67, in to_list cursor = self.motor_cursor ^^^^^^^^^^^^^^^^^ File "my-project/.venv/lib/python3.12/site-packages/beanie/odm/queries/find.py", line 661, in motor_cursor self.build_aggregation_pipeline() File "my-project/.venv/lib/python3.12/site-packages/beanie/odm/queries/find.py", line 610, in build_aggregation_pipeline construct_lookup_queries( File "my-project/.venv/lib/python3.12/site-packages/beanie/odm/utils/find.py", line 29, in construct_lookup_queries construct_query( File "my-project/.venv/lib/python3.12/site-packages/beanie/odm/utils/find.py", line 271, in construct_query "from": link_info.document_class.get_motor_collection().name, # type: ignore ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "my-project/.venv/lib/python3.12/site-packages/pydantic/_internal/_model_construction.py", line 242, in __getattr__ raise AttributeError(item) AttributeError: get_motor_collection
注:移除fetch_links=True后,调用await project.fetch_all_links()可正常运行;数据库初始化代码如下,且已在FastAPI lifespan启动阶段执行:
async def init_db(db_url: str): client = AsyncIOMotorClient(host=db_url, tz_aware=True) await init_beanie( database=client.get_default_database(default='myDB'), document_models=[Project, ItemDB], allow_index_dropping=True )
使用版本:FastAPI 0.115.2、Beanie 1.27.0
原因分析
这是Beanie 1.27.0版本的已知Bug:使用fetch_links=True预取关联数据时,内部构建聚合管道的逻辑无法正确访问已初始化的关联模型集合对象;而fetch_all_links()是在文档实例上执行,此时模型已完成数据库绑定,因此可以正常工作。
解决方案
- 临时替代方案:先获取Project实例,再调用
fetch_all_links(),虽为两次数据库操作,但可正常运行:
@router.get('/{project_id}', status_code=status.HTTP_200_OK, summary='Get a project', response_model_exclude_none=True) async def get_project(project_id: str) -> Project: project = await Project.get(project_id) if not project: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) await project.fetch_all_links() return project
- 升级Beanie版本:该Bug已在Beanie 1.27.1及以上版本修复,直接升级到最新稳定版即可:
pip install --upgrade beanie>=1.27.1
内容的提问来源于stack exchange,提问作者Triet Doan
相关产品推荐
相关产品推荐

