[Python] AttributeError: get_motor_collection when setting fetch_links=True

Discussão em 'Python' iniciado por Stack, Abril 18, 2025.

  1. Stack

    Stack Membro Participativo

    I'm using Beanie with FastAPI. For example, my Beanie models look like this:

    class Project(Document):
    id: PydanticObjectId = Field(default_factory=PydanticObjectId)
    items: list[Link[ItemDB]] | None = None

    class ItemDB(Document):
    creator: Indexed(str)


    When getting a Project by ID, I also want to prefetch its related items. So, my endpoint looks like this:

    @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


    But I got the error:

    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


    If I remove fetch_links=True and later on call

    await project.fetch_all_links()


    then it works fine.

    I'm sure that the database was initialized properly because everything else works normally, only this fetch_links has problem. Just to be sure, here is how the database was initialized:

    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
    )


    This function is then called in the startup part of FastAPI:

    @asynccontextmanager
    async def lifespan(app: FastAPI):
    await init_db(settings.db_url)
    yield


    app = FastAPI(
    lifespan=lifespan
    )


    So, did I miss something? Or it's a bug of Beanie? I'm using FastAPI 0.115.2 and Beanie 1.27.0.

    Continue reading...

Compartilhe esta Página