sqlalchemy关系primaryjoin与self和parent列

时间:2018-01-03 15:42:41

标签: python sqlalchemy

如果我有模特:

class A(Base):
    a_id = Column(Integer, primary_key=True)
    other = Column(Text)
    name = Column(Text)


class B(Base):
    b_id = Column(Integer, primary_key=True)
    other = Column(Text)


class C(Base):
    c_id = Column(Integer, primary_key=True)
    b_id = Column(ForeignKey(B.b_id))
    b = relationship(B)
    name = Column(Text)

    a = relationship(
        A,
        primaryjoin=and_(
            A.name == foreign(name),
            A.other == foreign(B.other)))

如何使关系C.a发挥作用,使session.query(C).options(joinedload(C.a))不会失败。

在纯SQL中我会这样做:

select * from c 
join b using(b_id) 
join a on a.name = c.name and a.other = b.other

a.name, a.other有一个独特的约束,所以我知道每a我会获得1或0 c个。{/ p>

我想我需要以某种方式使用secondary=,但我能找到的所有例子都是纯粹的多对多示例。

谢谢!

1 个答案:

答案 0 :(得分:2)

您必须使用relationship to a non primary mapper而不是辅助,因为:

  

有一个复杂的连接案例,即使这种技术(composite "secondary" join)还不够;当我们寻求从A加入B时,在其间使用任意数量的CD等,但是也有加入条件在AB 之间直接 。在这种情况下,从AB的联接可能很难用复杂的primaryjoin条件来表达,因为中间表可能需要特殊处理,而它也是不能用secondary对象表达,因为A->secondary->B模式不支持AB之间的任何引用。

因此,根据文档中的示例,我们可以使用non primary mapper来构建您的关系,该{{3}}将类A映射到表ab之间的联接:

from sqlalchemy import create_engine, Column, Integer, Text, ForeignKey, join, and_
from sqlalchemy.orm import relationship, mapper, sessionmaker, foreign
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine('sqlite:///', echo=True)
Base = declarative_base()
Base.metadata.bind = engine
Session = sessionmaker()

class A(Base):
    __tablename__ = 'a'
    a_id = Column(Integer, primary_key=True)
    other = Column(Text)
    name = Column(Text)


class B(Base):
    __tablename__ = 'b'
    b_id = Column(Integer, primary_key=True)
    other = Column(Text)

j = join(A, B, A.other == B.other)
A_viab = mapper(A, j, non_primary=True, properties={
    "other": [j.c.a_other, j.c.b_other]
})


class C(Base):
    __tablename__ = 'c'
    c_id = Column(Integer, primary_key=True)
    b_id = Column(ForeignKey(B.b_id))
    b = relationship(B)
    name = Column(Text)

    a = relationship(
        A_viab,
        primaryjoin=and_(foreign(name) == A_viab.c.name,
                         b_id == A_viab.c.b_id))

并在行动中:

In [4]: session.add(A(name='name', other='other'))

In [5]: session.add(C(name='name', b=B(other='other')))

In [6]: session.commit()
...

In [7]: c = session.query(C).options(joinedload(C.a)).first()
2018-01-04 15:10:21,338 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2018-01-04 15:10:21,340 INFO sqlalchemy.engine.base.Engine SELECT c.c_id AS c_c_id, c.b_id AS c_b_id, c.name AS c_name, a_1.other AS a_1_other, b_1.other AS b_1_other, a_1.a_id AS a_1_a_id, a_1.name AS a_1_name, b_1.b_id AS b_1_b_id 
FROM c LEFT OUTER JOIN (a AS a_1 JOIN b AS b_1 ON a_1.other = b_1.other) ON c.name = a_1.name AND c.b_id = b_1.b_id
 LIMIT ? OFFSET ?
2018-01-04 15:10:21,340 INFO sqlalchemy.engine.base.Engine (1, 0)

In [8]: c.a
Out[8]: <cplxjoin.A at 0x7f0c81599630>

生成的查询与手动SQL不完全相同,但afaic应该是等效的。