SQLAlchemy 关系映射

4 投票
2 回答
7351 浏览
提问于 2025-04-17 07:13

你好,我有个简单的问题——我有两个表(地址和用户——每个用户有一个地址,但很多用户可以住在同一个地址)。我创建了一个 sqlalchemy 的映射,像这样:

    class Person(object):
'''
classdocs
'''
 idPerson = Column("idPerson", Integer, primary_key = True)
 name = Column("name", String)
 surname = Column("surname", String)
 idAddress = Column("idAddress", Integer, ForeignKey("pAddress.idAddress"))
 idState = Column("idState", Integer, ForeignKey("pState.idState"))
 Address = relationship(Address, primaryjoin=idAddress==Address.idAddress)

class Address(object):
'''
Class to represent table address object
'''
 idAddress = Column("idAddress", Integer, primary_key=True)
 street = Column("street", String)
 number = Column("number", Integer)
 postcode = Column("postcode", Integer)
 country = Column("country", String) 
 residents = relationship("Person",order_by="desc(Person.surname, Person.name)", primaryjoin="idAddress=Person.idPerson") 




    self.tablePerson = sqlalchemy.Table("pPerson", self.metadata, autoload=True)
    sqlalchemy.orm.mapper(Person, self.tablePerson)
    self.tableAddress = sqlalchemy.Table("pAddress", self.metadata, autoload=True)
    sqlalchemy.orm.mapper(Address, self.tableAddress) 


myaddress = session.query(Address).get(1);
print myaddress.residents[1].name

当我获取会话并尝试查询一些内容时,

我遇到了一个错误:TypeError: 'RelationshipProperty' 对象不支持索引。

我明白 residents 是用来定义关系的,但我到底该怎么才能获取到与给定地址关联的居民列表呢?!谢谢!

2 个回答

0

你可以试着先定义地址(Address),再定义人(Person),并在里面加一个指向地址的反向引用(backref)——这样就能创建一个数组元素:

class Address(object):
 __tablename__ = 'address_table'
 idAddress = Column("idAddress", Integer, primary_key=True)

class Person(object):
 idPerson = Column("idPerson", Integer, primary_key = True)
 ...
 address_id = Column(Integer, ForeignKey('address_table.idAddress'))
 address = relationship(Address, backref='residents')

然后你就可以进行查询了:

myaddress = session.query(Address).get(1);
for residents in myaddress.residents:
  print name

另外,如果一个地址有很多居民,你还可以使用连接(join)来进一步筛选:

resultset = session.query(Address).join(Address.residents).filter(Person.name=='Joe')
# or
resultset = session.query(Person).filter(Person.name=='Joe').join(Person.address).filter(Address.state='NY')
and resultset.first() or resultset[0] or resultset.get(...) etc...
3

你在错误的地方定义了关系。我觉得你把声明式扩展非声明式的用法搞混了:

  1. 使用声明式时,你应该在你的模型中定义关系。
  2. 否则,你需要在将模型映射到时定义它们。

如果你正在做第二种情况,那么你需要从模型中删除两个relationship的定义,并将其添加到映射器中(只需要一个就够了):

mapper(Address, tableAddress,
properties={'residents': relationship(Person, order_by=(desc(Person.name), desc(Person.surname)), backref="Address"),}
) 

关于上面的代码,还有几点需要注意:

  1. 关系只在一边定义。backref会处理另一边的关系。
  2. 你不需要指定primaryjoin(只要你已经指定了外键,SQLAlchemy就能推断出列)。
  3. 你的order_by配置不正确,查看上面的代码以获取正确的版本。

撰写回答