我试图了解是否可以使用Sqlalchemy做某事,或者我是否以错误的方式思考它。举个例子,假设我有两个(这些只是示例)类:
class Customer(db.Model):
__tablename__ = 'customer'
id = Column(Integer, primary_key=True)
name = Column(String)
addresses = relationship('Address')
class Address(db.Model):
__tablename__ = 'address'
if = Column(Integer, primary_key=True)
address = Column(String)
home = Column(Boolean)
customer_id = Column(Integer, ForeignKey('customer.id'))
后来我想执行一个查询来获取客户和他们的家庭住址。有可能用这样的东西做到这一点:
db.session.query(Customer).join(Address, Address.home == True)
以上是否会进一步细化/限制连接,以便结果只能获得家庭地址?
答案 0 :(得分:1)
如果您怀疑查询构造是否符合要求,请尝试打印它:
In [29]: db.session.query(Customer).join(Address, Address.home == True)
Out[29]: <sqlalchemy.orm.query.Query at 0x7f14fa651e80>
In [30]: print(_)
SELECT customer.id AS customer_id, customer.name AS customer_name
FROM customer JOIN address ON address.home = true
很明显,这不是你想要的。每个客户都加入了每个家庭住址。由于实体的处理方式,这一开始可能并不明显。每个客户的重复行都会被忽略,即使基础查询错误,您也会获得不同客户实体的结果。在形成结果时,查询也有效地忽略了连接的地址。
最简单的解决方案是使用所需条件查询客户和地址元组:
db.session.query(Customer, Address).\
join(Address).\
filter(Address.home)
你也可以这样做
db.session.query(Customer).\
join(Address, (Customer.id == Address.customer_id) & Address.home).\
options(contains_eager(Customer.addresses))
但我强烈建议不要这样做。你会对自己关于收集的内容所撒谎,并且可能会在某些时候适得其反。相反,您应该使用custom join condition
为客户添加新的一对一关系class Customer(db.Model):
...
home_address = relationship(
'Address', uselist=False,
primaryjoin='and_(Customer.id == Address.customer_id, Address.home)')
然后你可以使用加入的热切加载
db.session.query(Customer).options(joinedload(Customer.home_address))
答案 1 :(得分:0)
是的,这完全有可能,但你可能想要代码如下:
# if you know the customer's database id...
# get the first address in the database for the given id that says it's for home
home_address = db.session.query(Address).filter_by(customer_id=customer_id_here, home=True).first()
您可以尝试使用枚举替换“类型”行,而不是使用布尔值。这可以让你轻松选择工作地点的地址,而不仅仅是“这个地址是否为家庭”的二元选择。
更新:您可能还会考虑在关系调用中使用back_populates关键字参数,因此如果您有一个地址实例(称为a),您可以通过类似a.customer(这是一个实例)来获取客户的实例。与此地址相关联的Customer类。)