Python 联接上的筛选器不会传播到相关的orm.relations
在此定义一些基本结构:Python 联接上的筛选器不会传播到相关的orm.relations,python,sqlalchemy,Python,Sqlalchemy,在此定义一些基本结构: class A( Base ): __tablename__ = 'a' id = Column( types.Integer(), primary_key = True ) abs = orm.relation( 'AB', lazy='joined' ) class AB( Base ): __tablename__ = 'ab' id = Column(
class A( Base ):
__tablename__ = 'a'
id = Column( types.Integer(), primary_key = True )
abs = orm.relation( 'AB', lazy='joined' )
class AB( Base ):
__tablename__ = 'ab'
id = Column( types.Integer(), primary_key = True )
a_id = Column( types.Integer(), ForeignKey( 'a.id' ) )
b_id = Column( types.Integer(), ForeignKey( 'b.id' ) )
a = orm.relation( 'A' )
b = orm.relation( 'B', lazy='joined' )
class B( Base ):
__tablename__ = 'b'
id = Column( types.Integer(), primary_key = True )
bas = orm.relation( 'AB' )
现在假设我有一个A
,有多个B
s与其相关(比如A.id=1
),我想根据这些B
进行过滤。我执行以下查询:
a = db.session.query( A ).join( A.abs ).filter( AB.b_id = 1, A.id = 1 ).first()
此时,我希望len(a.abs)==1,但事实并非如此。换句话说,应用于联接的筛选器不会传播到orm.relation。如何获得此行为?中描述了此问题的原因,归结为:查询中将有两个不同的联接。一个用于构建要筛选的正确联接(这是您使用
.join(A.abs)
生成的联接),另一个用于加载关系(ORM根据lazy=“joined”
自动插入该关系,否则它将在acess上查询它)
现在有几种方法可以解决这个问题。但首先你应该考虑你真正想要什么。因为当你说A.abs
时,你实际上是说“所有属于这个A的AB条目”。但是当您指定一个b_id
时,这并不是您想要的,因为这不是这种关系所代表的。因此,这里有一个干净的方法:
db.session.query(A, AB).join(A.abs).filter(AB.b_id = 1, A.id = 1)
现在,您将从查询中获得AB
作为第二个返回的对象。这就是正确的方法,因为在A.abs
中只有一个AB
,实际上是在欺骗ORM:这里不是这样(它可能会破坏东西)。然而,如果你坚持这样做是可能的。您可以通过以下方式禁用双连接:
这将产生一个A.abs
,其中只有一个条目具有b_id=1
。然而,正如已经说过的,这不是一个好的解决方案,也不是你应该做的
作为补充提示,我建议您在引擎中启用
echo=True
甚至echo=“debug”
,以确保看到正在执行的查询。如果查看原始查询,那么将在同一个表上看到两个联接。中描述了此问题的原因,归结起来就是:查询中将有两个不同的联接。一个用于构建要筛选的正确联接(这是您使用.join(A.abs)
生成的联接),另一个用于加载关系(ORM根据lazy=“joined”
自动插入该关系,否则它将在acess上查询它)
现在有几种方法可以解决这个问题。但首先你应该考虑你真正想要什么。因为当你说A.abs
时,你实际上是说“所有属于这个A的AB条目”。但是当您指定一个b_id
时,这并不是您想要的,因为这不是这种关系所代表的。因此,这里有一个干净的方法:
db.session.query(A, AB).join(A.abs).filter(AB.b_id = 1, A.id = 1)
现在,您将从查询中获得AB
作为第二个返回的对象。这就是正确的方法,因为在A.abs
中只有一个AB
,实际上是在欺骗ORM:这里不是这样(它可能会破坏东西)。然而,如果你坚持这样做是可能的。您可以通过以下方式禁用双连接:
这将产生一个A.abs
,其中只有一个条目具有b_id=1
。然而,正如已经说过的,这不是一个好的解决方案,也不是你应该做的
作为补充提示,我建议您在引擎中启用
echo=True
甚至echo=“debug”
,以确保看到正在执行的查询。如果查看原始查询,那么将在同一个表上看到两个联接。对于第一个解决方案,我还需要手动将联接条件添加到筛选器中,不是吗?例如db.session.query(A,AB).filter(AB.A_id=A.id,AB.b_id=1,A.id=1)
如果省略了join
部分,则必须手动执行此操作,否则SQLAlchemy可以为您执行此操作。对于第一个解决方案,我还需要手动将连接条件添加到过滤器中,不是吗?例如db.session.query(A,AB).filter(AB.A_id=A.id,AB.b_id=1,A.id=1)
如果省略了join
部分,则必须手动执行此操作,否则SQLAlchemy可以为您执行此操作。