【问题标题】:greenlet_spawn has not been calledgreenlet_spawn 尚未被调用
【发布时间】:2022-12-18 01:41:08
【问题描述】:

我正在尝试获取一对多关系中匹配的行数。当我尝试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 need 0 here, as of now this parent has no children

asyncio.run(main())

【问题讨论】:

    标签: python sqlite sqlalchemy sqlmodel


    【解决方案1】:

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

    解决此问题的一种方法是在关系定义中指定 load strategy 而不是“惰性”。使用 SQLModel,这看起来像:

    children: list['Child'] = Relationship(
        back_populates='parent', sa_relationship_kwargs={'lazy': 'selectin'}
    )
    

    这将导致 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
    

    【讨论】:

    • 感谢@snakecharmerb 的回复,这有效,如果我将 sa_relationship_kwargs={'lazy': 'selectin'} 添加到我的模型中,我是否必须运行 alembic 并创建一个迁移脚本,当前表的配置没有 sa_relationship_kwargs
    • 关系完全存在于Python层,所以应该不需要迁移。
    【解决方案2】:

    我有同样的问题。我在我的模型中添加“sa_relationship_kwargs={'lazy':'selectin'},使用 select(model).where(model.id==id).options(selectinload(model.children).i得到同样的错误,greenlet_spawn 没有被调用!如何解决?这是我的问题:Use AsyncSession Sqlalchemy,Error:sqlalchemy.exc.MissingGreenlet: greenlet_spawn has not been called; can't call await_only() here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-19
      • 1970-01-01
      相关资源
      最近更新 更多