我有两个类A和B,它们有两种不同的关系:
1)多对多关系,使用关联表(关联)来存储仅与该特定关联相关的信息(association_property_1),并通过A和B中的backref实例化。
2)使用table_b中的外键在A和B之间建立一对一的关系,这样只有B'知道'这种关系。我不在乎A是否知道它,但这样看似简单。
我的课程如下:
class A(Base):
__tablename__ = 'table_a'
id = Column(Integer, primary_key=True)
a_property_1 = Column(Float)
a_property_2 = Column(Float)
a_property_special = Column(Float)
# Many-to-many relationship with B through an Association
associated_bs = relationship('Association', backref='a')
class B(Base):
__tablename__ = 'table_b'
id = Column(Integer, primary_key=True)
b_property_1 = Column(Float)
b_property_2 = Column(Float)
# One-to-one relationship with A
a_id = Column(Integer, ForeignKey('table_a.id'))
a = relationship('A', uselist=False, backref='b')
# Many-to-many relationship with A through an Association
associated_as = relationship('Association', backref='b')
class Association(Base):
__tablename__ = 'associations'
a_id = Column(Integer, ForeignKey('table_a.id'), primary_key=True)
b_id = Column(Integer, ForeignKey('table_b.id'), primary_key=True)
association_property_1 = Column(Float)
我想对所有关联运行查询,我可以通过与B的一对一关系访问A的特殊属性。所以基本上我希望能够访问该属性
B.a.a_property_special
内部查询。
特定查询的示例可能如下:
session.query(Association.association_property_1,
func.abs(A.a_property_special - B.a.a_property_special).\
filter(B.a.a_property_special > 3.0)
其中A和B使用多对多关系连接,而B.a通过一对一连接。显然这个查询不起作用,因为B没有实例化,所以我将无法访问B.a.a_property_special。
如果我没有多对多关系,我可以加入A对B并完成它。我的问题是我想使用关联查询A和B,但我仍然需要标量B.a.a_property_special通过一对一的关系。
我尝试了几种不同的解决方案,但由于各种原因,所有解决方案都被证明是不能令人满意的。
我觉得这很简单,但我似乎无法找到一个好的解决方案。欢迎任何帮助或评论。
答案 0 :(得分:6)
SQLAlchemy明确关于连接,所以当你看到类似的东西时:
session.query(B).filter(B.a.a_property_special > 3.0)
这实际上意味着:
session.query(B).join(B.a).filter(A.a_property_special > 3.0)
还有一个子查询案例,它不如连接效率高。子查询案例总是需要使用相关的子查询,如下所示:
subq = session.query(A.a_property_special).where(A.id == B.a_id).correlate(B).as_scalar()
session.query(B).filter(subq > 3.0)
使用关系时,您还可以访问any()和has()方法,这些方法分别呈现一对多,多对一的EXISTS子查询:
session.query(B).filter(B.a.has(A.a_property_special > 3.0))
以上就相当于:
from sqlalchemy import exists
session.query(B).filter(exists().where(B.a_id==A.id, A.a_property_special > 3.0))
子查询的优点是它可以用于创建自包含的过滤条件,而当依赖于join()时,没有办法隐式发生。但是子查询方法在数据库方面表现不佳。
当然有很多简单的情况,可以根据存在的各种事物将连接隐式添加到封闭查询中,这就像Django那样的ORM,但是SQLAlchemy对它的看法是你很快进入了像这样简单化的方法发生故障的情况,所以我们不会在库中做出类似的猜测。
所以要采用原始查询示例:
session.query(Association.association_property_1,
func.abs(A.a_property_special - B.a.a_property_special)).\
filter(B.a.a_property_special > 3.0)
你实际上是想用两种不同的方式来攻击A,所以在进行显式连接路由时,你需要为它做一个别名,以便它可以被定位两次:
from sqlalchemy.orm import aliased
a_alias = aliased(A)
session.query(
Association.association_property_1,
func.abs(A.a_property_special - a_alias.a_property_special)
).\
join(Association.a).\
join(Association.b).join(a_alias, B.a).\
filter(a_alias.a_property_special > 3.0)
这构建方式基本上与在SQL中执行的方式相同。 SQL是这样的:
SELECT associations.association_property_1 AS associations_association_property_1, abs(table_a.a_property_special - table_a_1.a_property_special) AS abs_1
FROM associations JOIN table_a ON table_a.id = associations.a_id JOIN table_b ON table_b.id = associations.b_id JOIN table_a AS table_a_1 ON table_a_1.id = table_b.a_id
WHERE table_a_1.a_property_special > :a_property_special_1
这里的子查询路由对数据库来说很难。虽然你可以连接渲染子查询的Association上的属性,但是它们都需要被称为相关子查询,如果你在一个查询中多次引用它们,它们的表现就会非常糟糕。以下是使用hybrid attributes:
的方法class Association(Base):
__tablename__ = 'associations'
a_id = Column(Integer, ForeignKey('table_a.id'), primary_key=True)
b_id = Column(Integer, ForeignKey('table_b.id'), primary_key=True)
association_property_1 = Column(Float)
@hybrid.hybrid_property
def a_property_special(self):
return self.a.a_property_special
@a_property_special.expression
def a_property_special(cls):
return select([A.a_property_special]).where(A.id==cls.a_id).as_scalar()
@hybrid.hybrid_property
def b_a_property_special(self):
return self.b.a.a_property_special
@b_a_property_special.expression
def b_a_property_special(cls):
return select([A.a_property_special]).where(A.id==B.a_id).where(B.id==cls.b_id).as_scalar()
session.query(
Association.association_property_1,
func.abs(Association.a_property_special - Association.b_a_property_special)
)
这里的SQL是:
SELECT associations.association_property_1 AS associations_association_property_1, abs((SELECT table_a.a_property_special
FROM table_a
WHERE table_a.id = associations.a_id) - (SELECT table_a.a_property_special
FROM table_a, table_b
WHERE table_a.id = table_b.a_id AND table_b.id = associations.b_id)) AS abs_1
FROM associations
为了查询的目的,数据库给出的关于这三个表中的行如何相互关联的信息较少,因此在获取行时它必须做更多的工作。连接情况,虽然它要求您以两种不同的方式将“A”布局为目标,并且还指定事物如何连接,为数据库提供更简单的任务,因为连接比计算每行的相关SELECT的相关性更有效父行集。