Sqlalichemy:在父属性中加载相关对象ID

时间:2012-02-14 10:55:14

标签: python orm sqlalchemy

使用以下表格和映射

class A:
    def __init__(self):
        self.id = None
        self.b_ids = {}
        self.b_s = {}

class B:
    def __init__ (self):
        self.id = None
        self.a_id = None
        self.a = None

a = Table('t_a', meta,
    Column('id',Integer, autoincrement=True, primary_key=True),
    )

b = Table('t_b', meta,
    Column('id',Integer, autoincrement=True, primary_key=True),
    Column('a_id', Integer, ForeignKey('t_a.id')),
    )

mapper(A, a)
mapper(B, b, properties={'a' : relationship(A, backref="b_s")})

当我加载'A'时,我可以在'b_s'属性中获得相关的'B'对象。但我想要的是A.b_ids属性中相关B的ID列表。有没有办法做到这一点?

我试过了:

mapper(A, a, properties={'b_ids' : 
                         column_property(select(
                                                [b.c.id], 
                                                a.c.id==b.c.a_id))
                         })

但它给出错误:'ProgrammingError:(ProgrammingError)由用作表达式的子查询返回的多行[

2 个答案:

答案 0 :(得分:4)

column_property()用于向SELECT语句添加另一列,例如:

SELECT a.x, a.y, (SELECT b.id FROM b where a.id=b.a_id) AS b_id FROM a

在SQL中,这是SELECT语句的columns子句中的子查询,它需要为每一行返回一行/列 - 在SQLAlchemy中,我们将其称为“标量选择”。

在这种情况下,您正在寻找一个列表,因此没有嵌入到主列子句中。这就是数据库为此返回错误的原因。您的映射上已经有一个“b ID”列表,格式为“b_s”。您只想从中提取“id”,这可以通过关联代理轻松完成:

from sqlalchemy.ext.associationproxy import association_proxy

class A(object):
    # ... other things
    b_ids = association_proxy('b_s', 'id')

当你访问“a.b_ids”时,它会查看“b_s”中的每个条目并提取“id”属性,返回一个与“b_s”集合类型兼容的集合,在这种情况下是一个Python列表。

答案 1 :(得分:0)

我尝试将association_proxy放在mapper属性中。但它不允许。炼金术文档也说“代理是一个Python属性,与mapper关系不同,在你的类中定义”。所以我假设我们不能保持这个内部mapper()的映射。

无论如何,我无法将模型类修改为其生成的代码。所以我尝试了下面的内容:

a = Table('t_a', meta,
    Column('id',Integer, autoincrement=True, primary_key=True),
    )

b = Table('t_b', meta,
    Column('id',Integer, autoincrement=True, primary_key=True),
    Column('a_id', Integer, ForeignKey('t_a.id')),
    )

mapper(A, a)
mapper(B, b, properties={'a' : relationship(A, backref="b_s")})

A.b_ids=association_proxy('b_s', 'id')

这似乎有效。这样做是可以的吗?或者有更好的方法吗?

此致 Litty