使用别名来解释SQLAlchemy ORM Query中的Keyed Tuple输出

时间:2014-07-03 20:03:57

标签: orm sqlalchemy

请使用别名类帮助我改进/理解查询。考虑一个在两个位置之间移动的示例,如下所述。

class Location(Base):
  __tablename__ = 'location'
  id = Column(Integer, primary_key = True)

class Movement(Base):
  __tablename__ = 'movement'
  id = Column(Integer, primary_key = True)
  from_id = Column(None, ForeignKey('location.id')
  to_id = Column(None, ForeignKey('location.id')

  from_location = relationship('Location', foreign_keys = from_id)
  to_location = relationship('Location', foreign_keys = to_id)

要在查询中连接三个表,我正在使用sqlalchemy.orm中的aliased()函数:

FromLocation = aliased(Location)
ToLocation = aliased(Location)

r = session.query(Movement, FromLocation, ToLocation).\
    join(FromLocation, Movement.from_id == FromLocation.id).\
    join(ToLocation, Movement.to_id == ToLocation.id).first()

第一个问题是“使用r的智能方式是什么?”查询返回一个键控元组,但唯一的键是'Movement',没有像我期望的那样'FromLocation'。我可以用r [1]得到它,但这很容易被打破。

第二个问题是“我的关系是否合适?”我不认为我必须明确指定连接目标。但是如果没有指定目标,我会收到错误:

r = session.query(Movement, FromLocation, ToLocation).\
    join(FromLocation).\
    join(ToLocation)

InvalidRequestError: Could not find a FROM clause to join from.  Tried joining to <AliasedClass at 0x10cfa16d8; Location>, but got: Can't determine join between 'movement' and '%(4512717680 location)s'; tables have more than one foreign key constraint relationship between them. Please specify the 'onclause' of this join explicitly.

是的,我看到了两个外键,但是如何正确映射它们?

1 个答案:

答案 0 :(得分:2)

选项-1:要在KeyedTuple中添加名称,只需为别名添加名称:

FromLocation = aliased(Location, name="From")
ToLocation = aliased(Location, name="To")

# ...
print(r.keys)
# >>>> ['Movement', 'From', 'To']

选项-2:创建一个查询以仅返回Movement个实例,但预先加载这两个位置。请注意另外join语法,指定relationship而不是密钥对。

r = (session.query(Movement)
        .join(FromLocation, Movement.from_location)
        .join(ToLocation, Movement.to_location)
        .options(contains_eager(Movement.from_location, alias=FromLocation))
        .options(contains_eager(Movement.to_location, alias=ToLocation))
    ).first()
print(r)
print(r.from_location)
print(r.to_location)