正确访问SQLAlchemy关系

时间:2015-09-21 17:41:40

标签: python sqlalchemy

以下示例代码(基于SQLAlchemy ORM tutorial):

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, backref
from sqlalchemy.orm import sessionmaker
from sqlalchemy import func

engine = create_engine('sqlite:///:memory:')

Session = sessionmaker(bind=engine)

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)

class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    email_address = Column(String, nullable=False)
    user_id = Column(Integer, ForeignKey('users.id'))
    user = relationship("User", backref=backref('addresses', order_by=id))    

Base.metadata.create_all(engine)


if __name__ == "__main__":

    session = Session()
    query = session.query(func.count(User.id)).join(User.addresses)

在0.8之前的SQLAlchemy版本上运行良好。但是对于版本> = 0.8,它会产生以下错误:

AttributeError: type object 'User' has no attribute 'addresses'

如果最后一行是:

,则不会发生这种情况
    query = session.query(User).join(User.addresses)

但是,根据SQLAlchemy文档,func表达式的用法为the preferred way to issue 'count' queries(另请参阅zzzeek's answer here)。对于大型桌子来说,它确实很重要。

所以,我知道这个问题是因为new way relationships work since version 0.8而发生的。问题是,“强制执行”backref初始化的正确(和最有效)方法应该是什么。

注意:在使用映射类作为参数发出第一个查询之后,将初始化backrefs,以便所有其他查询不会遇到上述问题。关键是,在发出func.count查询时,我不知道backrefs是否已经初始化(即这是第一次发出查询)。

1 个答案:

答案 0 :(得分:1)

您的示例与SQLAlchemy文档的Linking Relationships with Backref部分中的示例完全相同。第二个代码块使用back_populates参数显示equivalent版本。但在这种情况下,关系的两端都已初始化,您的查询应该可以正常工作。使用order_by,您的模型应如下所示:

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    addresses = relationship("Address", order_by="Address.id", back_populates="user")


class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    email_address = Column(String, nullable=False)
    user_id = Column(Integer, ForeignKey('users.id'))
    user = relationship( "User", back_populates="addresses")