对自动加入的人应用过滤器 table
Apply a filter to an automatically joined table
这是我的 SQL 设置
create table a
(
id serial primary key,
ta text
);
create table b
(
id serial primary key,
tb text,
aid integer references a(id) not null
);
Python:
import sqlalchemy as sa
import sqlalchemy.orm
connection_url = "..."
engine = sa.create_engine(connection_url, echo=True, future=True)
mapper_registry = sa.orm.registry()
class A:
pass
class B:
pass
mapper_registry.map_imperatively(
B,
sa.Table(
'b',
mapper_registry.metadata,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('tb', sa.String(50)),
sa.Column('aid', sa.ForeignKey('a.id')),
))
mapper_registry.map_imperatively(
A,
sa.Table(
'a',
mapper_registry.metadata,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('ta', sa.String(50))
),
properties={
'blist': sa.orm.relationship(B, lazy='joined'),
},
)
with sa.orm.Session(engine) as session:
sel = sa.select(A)
cur = session.execute(sel)
for rec in cur.unique().all():
print(rec.A.ta, [b.tb for b in rec.A.blist])
到目前为止一切正常,但现在我需要对子表 (B
) 应用过滤器以仅包含符合条件的行。
sel = sa.select(A).where(?WHAT?.like('search'))
换句话说,我如何在 SqlAlchemy 中编写与以下 SQL 等效的内容?
SELECT *
FROM a
LEFT OUTER JOIN b ON a.id = b.aid
WHERE b.tb like 'search'
这个怎么样(我希望在目标 class 中生成空列表):
SELECT *
FROM a
LEFT OUTER JOIN b ON a.id = b.aid
AND b.tb like 'search'
答案 1
您不能直接过滤 joinedload
(请参阅 The Zen of Joined Eager Loading)。您需要添加一个连接到 table B
并过滤列:
sel = sa.select(A).join(B).filter(B.tb.like('search'))
这导致:
SELECT a.*, b_1.*
FROM a JOIN b ON a.id = b.aid
LEFT OUTER JOIN b AS b_1 ON a.id = b_1.aid
WHERE b.tb LIKE 'search'
你看到 Query.join() 的用法是提供后续查询条件中使用的 JOIN 子句,而关系中的 joinedload()
的用法仅加载集合,对于每个 B
在结果中。
答案 2
在这里,您可以使用 .options
包括 AND
条件,使用 joinedload
覆盖在映射器上配置的 joinedload
。
sel = sa.select(A).options(joinedload(A.blist.and_(B.tb.like('search'))))
这导致以下查询:
SELECT *
FROM a
LEFT OUTER JOIN b ON a.id = b.aid
AND b.tb LIKE 'search'
下面介绍的两个解决方案(针对 2 个问题)依赖于两个 sqlalchemy 函数:
sqlalchemy.orm.contains_eager
欺骗 sqlalchemy
所需的关系已经是查询的一部分;和
sqlalchemy.orm.Query.options
禁用关系上配置的默认 joinedload
。
问题 1
SELECT *
FROM a
OUTER JOIN b
ON a.id = b.aid
WHERE b.tb like 'search'
答案 1
是通过此查询实现的,并在行中进行了解释:
sel = (
sa.select(A)
# disable 'joinedload' if it remains configured on the mapper; otherwise, the line below can be removed
.options(sa.orm.lazyload(A.blist))
# join A.blist explicitely
.outerjoin(B, A.blist) # or: .outerjoin(B, A.id == B.aid)
# add the filter
.filter(B.tb.like('search'))
# trick/hint to SQ that the relationship objects are already returned in the query
.options(sa.orm.contains_eager(A.blist))
)
问题 2
SELECT *
FROM a
OUTER JOIN b
ON a.id = b.aid
AND b.tb like 'search'
答案 2
是通过此查询实现的,并附有解释,但基本上 .filter
条件已移动到 join
条件中:
sel = (
sa.select(A)
# disable 'joinedload' if it remains configured on the mapper; otherwise, the line below can be removed
.options(sa.orm.lazyload(A.blist))
# join A.blist explicitely including the filter
.outerjoin(B, sa.and_(B.aid == A.id, B.tb.like('search')))
# trick/hint to SQ that the relationship objects are already returned in the query
.options(sa.orm.contains_eager(A.blist))
)
警告:使用contains_eager
时要小心,并在明确定义的范围内使用它,因为你基本上是在“说谎”您可能未加载“所有”相关对象的 SA 模型。出于查询数据的目的,通常完全没问题,但修改和添加关系可能会导致一些奇怪的结果。
这是我的 SQL 设置
create table a
(
id serial primary key,
ta text
);
create table b
(
id serial primary key,
tb text,
aid integer references a(id) not null
);
Python:
import sqlalchemy as sa
import sqlalchemy.orm
connection_url = "..."
engine = sa.create_engine(connection_url, echo=True, future=True)
mapper_registry = sa.orm.registry()
class A:
pass
class B:
pass
mapper_registry.map_imperatively(
B,
sa.Table(
'b',
mapper_registry.metadata,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('tb', sa.String(50)),
sa.Column('aid', sa.ForeignKey('a.id')),
))
mapper_registry.map_imperatively(
A,
sa.Table(
'a',
mapper_registry.metadata,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('ta', sa.String(50))
),
properties={
'blist': sa.orm.relationship(B, lazy='joined'),
},
)
with sa.orm.Session(engine) as session:
sel = sa.select(A)
cur = session.execute(sel)
for rec in cur.unique().all():
print(rec.A.ta, [b.tb for b in rec.A.blist])
到目前为止一切正常,但现在我需要对子表 (B
) 应用过滤器以仅包含符合条件的行。
sel = sa.select(A).where(?WHAT?.like('search'))
换句话说,我如何在 SqlAlchemy 中编写与以下 SQL 等效的内容?
SELECT *
FROM a
LEFT OUTER JOIN b ON a.id = b.aid
WHERE b.tb like 'search'
这个怎么样(我希望在目标 class 中生成空列表):
SELECT *
FROM a
LEFT OUTER JOIN b ON a.id = b.aid
AND b.tb like 'search'
答案 1
您不能直接过滤 joinedload
(请参阅 The Zen of Joined Eager Loading)。您需要添加一个连接到 table B
并过滤列:
sel = sa.select(A).join(B).filter(B.tb.like('search'))
这导致:
SELECT a.*, b_1.*
FROM a JOIN b ON a.id = b.aid
LEFT OUTER JOIN b AS b_1 ON a.id = b_1.aid
WHERE b.tb LIKE 'search'
你看到 Query.join() 的用法是提供后续查询条件中使用的 JOIN 子句,而关系中的 joinedload()
的用法仅加载集合,对于每个 B
在结果中。
答案 2
在这里,您可以使用 .options
包括 AND
条件,使用 joinedload
覆盖在映射器上配置的 joinedload
。
sel = sa.select(A).options(joinedload(A.blist.and_(B.tb.like('search'))))
这导致以下查询:
SELECT *
FROM a
LEFT OUTER JOIN b ON a.id = b.aid
AND b.tb LIKE 'search'
下面介绍的两个解决方案(针对 2 个问题)依赖于两个 sqlalchemy 函数:
sqlalchemy.orm.contains_eager
欺骗sqlalchemy
所需的关系已经是查询的一部分;和sqlalchemy.orm.Query.options
禁用关系上配置的默认joinedload
。
问题 1
SELECT *
FROM a
OUTER JOIN b
ON a.id = b.aid
WHERE b.tb like 'search'
答案 1
是通过此查询实现的,并在行中进行了解释:
sel = (
sa.select(A)
# disable 'joinedload' if it remains configured on the mapper; otherwise, the line below can be removed
.options(sa.orm.lazyload(A.blist))
# join A.blist explicitely
.outerjoin(B, A.blist) # or: .outerjoin(B, A.id == B.aid)
# add the filter
.filter(B.tb.like('search'))
# trick/hint to SQ that the relationship objects are already returned in the query
.options(sa.orm.contains_eager(A.blist))
)
问题 2
SELECT *
FROM a
OUTER JOIN b
ON a.id = b.aid
AND b.tb like 'search'
答案 2
是通过此查询实现的,并附有解释,但基本上 .filter
条件已移动到 join
条件中:
sel = (
sa.select(A)
# disable 'joinedload' if it remains configured on the mapper; otherwise, the line below can be removed
.options(sa.orm.lazyload(A.blist))
# join A.blist explicitely including the filter
.outerjoin(B, sa.and_(B.aid == A.id, B.tb.like('search')))
# trick/hint to SQ that the relationship objects are already returned in the query
.options(sa.orm.contains_eager(A.blist))
)
警告:使用contains_eager
时要小心,并在明确定义的范围内使用它,因为你基本上是在“说谎”您可能未加载“所有”相关对象的 SA 模型。出于查询数据的目的,通常完全没问题,但修改和添加关系可能会导致一些奇怪的结果。