SQLAlchemy外键映射到id列表,而不是实体

时间:2015-03-27 13:13:32

标签: sqlalchemy

在通常的CustomerOrder示例中,这种SQLAlchemy代码......

data = db.query(Customer)\
    .join(Order, Customer.id == Order.cst_id)\
    .filter(Order.amount>1000)

...将提供与例如关联的客户模型的实例。大订单(amount > 1000)。生成的Customer实例还将包含其订单列表,因为在此示例中我们使用backref因为这个原因:

class Order:
    ...
    customer = relationship("customers", backref=backref('orders'))

问题在于,迭代Customer.orders意味着DB将返回Order的完整实例 - 基本上对Order的所有列执行'select *'。

如果由于性能原因,人们想要例如从Order中只读取1个字段(例如id),并且Customer实例中的.orders字段是一个简单的ID列表?

customers = db.query(Customer)....
...
pdb> print customers[0].orders
[2,4,7]

SQLAlchemy可以吗?

2 个答案:

答案 0 :(得分:0)

您可以做的是以这种方式进行查询:

(
    session.query(Customer.id, Order.id)
    .select_from(Customer)
    .join(Customer.order)
    .filter(Order.amount > 1000)
)

它不会像你提出的那样产生确切的结果,但它会给你一个类似于[(customer_id, order_id), ...]的元组列表。

我不完全确定您是否可以急切地将order_ids加载到Customer对象中,但我认为应该这样做,您可能需要查看joinedloadsubqueryload和如果有帮助,也许可以通过relationship-loading docs

在这种情况下,它可以写成;

(
    session.query(Customer)
    .select_from(Customer)
    .join(Customer.order)
    .options(db.joinedload(Customer.orders))
    .filter(Order.amount > 1000)
)

并使用noload来避免加载其他列。

答案 1 :(得分:0)

我最终做到了最佳 - 使用数组聚合:

data = db.query(Customer).with_entities(
    Customer,
    func.ARRAY_AGG(
        Order.id,
        type_=ARRAY(Integer, as_tuple=True)).label('order_ids')
    ).outerjoin(
        Orders, Customer.id == Order.cst_id
    ).group_by(
        Customer.id
    )

这将返回(CustomerEntity,list)的元组 - 这正是我想要的。