Issue
Here is my model:
class MyModel(Base):
__tablename__ = 'model'
id : Mapped[int] = mapped_column(primary_key=True)
given_id : Mapped[str] = mapped_column(String(50), unique=True, nullable=True)
cancel_id : Mapped[int] = mapped_column(ForeignKey('model.given_id'), nullable=True)
return_model: Mapped['MyModel'] = relationship(remote_side=[given_id])
With the structure like above when calling return_model
I'm getting the following error:
sqlalchemy.exc.MissingGreenlet: greenlet_spawn has not been called; can't call await_only() here. Was IO attempted in an unexpected place? (Background on this error at: https://sqlalche.me/e/20/xd2s)
But if I replace given_id
with id
then everything works fine and MyModel
is able to reference itself:
class MyModel(Base):
__tablename__ = 'model'
id : Mapped[int] = mapped_column(primary_key=True)
given_id : Mapped[str] = mapped_column(String(50), unique=True, nullable=True)
cancel_id : Mapped[int] = mapped_column(ForeignKey('model.id'), nullable=True)
return_model: Mapped['MyModel'] = relationship(remote_side=[id])
What is the right way of self-referencing in SQLAlchemy 2.0
? How to achieve a one-to-one relationship? What am I doing wrong?
Note: I'm using async engine.
Solution
Ok, so I've figured it out. When accessing related object/s the lazy loading will take place. According to the documentation:
in order to succeed will usually emit IO to the database, which will fail under asyncio as no implicit IO is allowed.
The suggested solution is for the Base
model to inherit from AsyncAttrs
:
class Base(AsyncAttrs, DeclarativeBase):
pass
This mixin adds a single new attribute AsyncAttrs.awaitable_attrs to all classes, which will yield the value of any attribute as an awaitable. This allows attributes which may be subject to lazy loading or deferred / unexpiry loading to be accessed such that IO can still be emitted
and now I can access my related object:
result = await instance.awaitable_attrs.return_model
Answered By - Taras Mykhalchuk
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.