MissingGreenlet:greenlet_spawn 尚未被调用

Jak*_*ake 15 python sqlite sqlalchemy python-asyncio sqlmodel

我正在尝试获取一对多关系中匹配的行数。当我尝试时parent.children_count我得到:

sqlalchemy.exc.MissingGreenlet:greenlet_spawn尚未被调用;不能在这里调用await_only()。是否在意想不到的地方尝试了 IO?(此错误的背景位于:https ://sqlalche.me/e/14/xd2s )

我添加了expire_on_commit=False但仍然遇到相同的错误。我怎样才能解决这个问题?

import asyncio
from uuid import UUID, uuid4
from sqlmodel import SQLModel, Relationship, Field
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession

class Parent(SQLModel, table=True):
    id: UUID = Field(default_factory=uuid4, primary_key=True)
    children: list["Child"] = Relationship(back_populates="parent")
    @property
    def children_count(self):
        return len(self.children)

class Child(SQLModel, table=True):
    id: UUID = Field(default_factory=uuid4, primary_key=True)
    parent_id: UUID = Field(default=None, foreign_key=Parent.id)
    parent: "Parent" = Relationship(back_populates="children")

async def main():
    engine = create_async_engine("sqlite+aiosqlite://")
    async with engine.begin() as conn:
        await conn.run_sync(SQLModel.metadata.create_all)

    async with AsyncSession(engine) as session:
        parent = Parent()
        session.add(parent)
        await session.commit()
        await session.refresh(parent)
        print(parent.children_count)  # I expect 0 here, as of now this parent has no children

asyncio.run(main())
Run Code Online (Sandbox Code Playgroud)

sna*_*erb 30

我认为这里的问题是,默认情况下 SQLAlchemy 延迟加载关系,因此parent.children_count隐式访问会触发数据库查询,从而导致报告的错误。

解决这个问题的一种方法是在关系定义中指定除“惰性”之外的加载策略。使用 SQLModel,这看起来像:

children: list['Child'] = Relationship(
    back_populates='parent', sa_relationship_kwargs={'lazy': 'selectin'}
)
Run Code Online (Sandbox Code Playgroud)

这将导致 SQLAlchemy 发出额外的查询来获取关系,同时仍处于“异步模式”。另一种选择是传递{'lazy': 'joined'},这将导致 SQLAlchemy 在单个查询中获取所有结果JOIN

如果不需要配置关系,您可以发出指定选项的查询:

from sqlalchemy.orm import selectinload
from sqlmodel import select

    ...
    async with AsyncSession(engine) as session:
        parent = Parent()
        session.add(parent)
        await session.commit()
        result = await session.scalars(
            select(Parent).options(selectinload(Parent.children))
        )
        parent = result.first()
        print(
            parent.children_count
        )  # I need 0 here, as of now this parent has no children
Run Code Online (Sandbox Code Playgroud)

  • 关系完全存在于Python层,因此不需要迁移。 (3认同)