SQLAlchemy 如何从 "aliased" table 加入 table

SQLAlchemy how to join a table from an "aliased" table

我已经在 SO 和 Google 以及官方的 SQLAlchemy 文档中阅读了类似的问题,但仍然无法弄清楚如何解决我的问题。

考虑以下结构(为简单起见删除了 non-relevant 个字段):

header_table = Table(
    'header',
    metadata,
    Column('id', Integer, primary_key=True),
    Column('parent_header_id', Integer)
)

item_table = Table(
    'item',
    dal.metadata,
    Column('id', Integer, primary_key=True),
    Column('header_id', Integer)
)

class Header:
    id: int
    parent_header_id: int
    # Relationships
    items: List[Item]
    children: List[Header]

class Item:
    id: int
    header_id: int

mapper(Header, header_table, properties={
    'children': relationship(Header, foreign_keys=[header_table.c.parent_header_id]),
})

总结一下:您可以嵌套 headers(最多 1 层嵌套),每个 header 可以有项目。

我正在尝试加载所有 header 及其项目和 children、 以及 children 的项目。

header_alias = aliased(Header)

records = (
    session.query(Header)
        .outerjoin(Header.items)
        .outerjoin(Header.children.of_type(header_alias))
        # .outerjoin(Header.children.of_type(header_alias).items) <<< THE PROBLEM IS HERE (READ BELOW)
        .options(contains_eager(Header.items))
        .options(contains_eager(Header.children.of_type(header_alias)))
        .all()
)

如何加载 children 的项目?

例子中注释掉的代码是错误的,我只是把它放在那里作为我正在尝试做的事情的例子。

注意:上面的代码 有效 ,但它是延迟加载 children 的项目,我正在尝试摆脱这种延迟加载。

非常感谢@zzzeek (Mike Bayer),SQLAlchemy 的作者,他回答了 Github 中的问题。

https://github.com/sqlalchemy/sqlalchemy/discussions/6876

OK you have to alias "items" also, this is SQL so every table has to be in the FROM clause only once. Here's a full running example

from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import aliased
from sqlalchemy.orm import contains_eager
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session

Base = declarative_base()

metadata = Base.metadata

header_table = Table(
    "header",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("parent_header_id", ForeignKey("header.id")),
)

item_table = Table(
    "item",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("header_id", ForeignKey("header.id")),
)


class Header(Base):
    __table__ = header_table
    children = relationship("Header")
    items = relationship("Item")


class Item(Base):
    __table__ = item_table
    id: int
    header_id: int


e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

s = Session(e)

s.add(
    Header(
        items=[Item(), Item()],
        children=[Header(items=[Item()]), Header(items=[Item(), Item()])],
    )
)
s.commit()
s.close()


header_alias = aliased(Header)
item_alias = aliased(Item)

records = (
    s.query(Header)
    .outerjoin(Header.items)
    .outerjoin(Header.children.of_type(header_alias))
    .outerjoin(header_alias.items.of_type(item_alias))
    .options(
        contains_eager(Header.items),
        contains_eager(Header.children.of_type(header_alias)).options(
            contains_eager(header_alias.items.of_type(item_alias))
        ),
    )
    .all()
)
s.close()

for r in records:
    print(r)
    print(r.items)
    for c in r.children:
        print(c)
        print(c.items)