Python sqlalchemy动态关系

时间:2017-09-03 18:20:44

标签: python sqlalchemy flask-sqlalchemy

我试图了解是否可以使用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)

以上是否会进一步细化/限制连接,以便结果只能获得家庭地址?

2 个答案:

答案 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类。)