joinedload polymorphic relationship: child-class columns are not loaded
#13201
-
|
SQLAlchemy 2.0.48 Hello, @zzzeek! Looks like I've encountered another polymorphic-bug - Is it the same as #13192 ? from sqlalchemy import String, select, ForeignKey, event
from sqlalchemy.orm import (
declarative_base, sessionmaker, mapped_column, Mapped, relationship, with_polymorphic,
joinedload, selectinload
)
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
Base = declarative_base()
class TopABC(Base):
__tablename__ = 'top_abc'
id: Mapped[str] = mapped_column(String(), primary_key=True)
name: Mapped[str] = mapped_column(String())
top: Mapped['Top'] = relationship(lambda: with_polymorphic(Top, '*', flat=True))
class Top(Base):
__tablename__ = 'top'
id: Mapped[str] = mapped_column(String(), ForeignKey(TopABC.id), primary_key=True)
type: Mapped[str] = mapped_column(String())
__mapper_args__ = dict(
polymorphic_on=type
)
class Foo(Top):
__tablename__ = 'foo'
id: Mapped[str] = mapped_column(String(), ForeignKey(Top.id), primary_key=True)
foo_name: Mapped[str] = mapped_column(String())
__mapper_args__ = dict(
polymorphic_identity='FOO'
)
class Bar(Top):
__tablename__ = 'bar'
id: Mapped[str] = mapped_column(String(), ForeignKey(Top.id), primary_key=True)
bar_name: Mapped[str] = mapped_column(String())
__mapper_args__ = dict(
polymorphic_identity='BAR'
)
async_engine = create_async_engine("sqlite+aiosqlite://", pool_pre_ping=True)
@event.listens_for(async_engine.sync_engine, "connect")
def enable_sqlite_fks(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
print("Foreign keys enabled for connection.")
AsyncSessionFactory = sessionmaker(
bind=async_engine, class_=AsyncSession, expire_on_commit=True, autoflush=False
)
async def create_tables() -> None:
async with async_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async def test():
async with AsyncSessionFactory() as sess:
sess.bind.echo = True
top_abc = TopABC()
top_abc.id = '1'
top_abc.name = 'some'
sess.add(top_abc)
await sess.flush()
foo = Foo(id='1', foo_name='fname')
sess.add(foo)
await sess.commit()
q = (
select(TopABC)
.options(
joinedload(TopABC.top) # not works
# selectinload(TopABC.top) # works
)
)
orm: TopABC
[orm] = (await sess.execute(q)).scalars().all()
assert isinstance(orm.top, Foo)
print(orm.top.foo_name) # emits sql-query if joinedload
async def main():
await create_tables()
await test()
import asyncio
asyncio.run(main())sql-echo: SELECT top_abc.id,
top_abc.name,
anon_1.id AS id_1,
anon_1.type
FROM top_abc
LEFT OUTER JOIN (
TOP AS anon_1 LEFT OUTER JOIN foo AS anon_2 ON anon_1.id = anon_2.id
LEFT OUTER JOIN bar AS anon_3 ON anon_1.id = anon_3.id
) ON top_abc.id = anon_1.id |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 9 replies
-
|
hiya - that this works at all, in any context, is absolute news to me: I can see why these days it works at all, because we added a feature called "relationship to aliased class", but we've never tested what you have there at all or tried to support that. What's supported is one of three approaches. the main one is to do this at the loader option, which tested works here: the others are to use polymorphic_load with "inline" or "selectin", which goes onto the subclass like this: that also works here |
Beta Was this translation helpful? Give feedback.
#13202