与 SQLAlchemy 的分支关系

Posted

技术标签:

【中文标题】与 SQLAlchemy 的分支关系【英文标题】:Branched relationships with SQLAlchemy 【发布时间】:2015-12-04 11:14:16 【问题描述】:

我有一个有点分支的关系结构(让我们使用一个匹配数据库的例子 - 不幸的是实际情况有点复杂):

class Hobby(Base):
    __tablename__ = 'hobby'
    id = Column(Integer, primary_key=True)
    hobby_name = Column(String(255, u'utf8_unicode_ci'), nullable=False)

class Guy(Base):
    __tablename__ = 'guy'
    id = Column(Integer, primary_key=True)
    name = Column(String(255, u'utf8_unicode_ci'), nullable=False)

class Girl(Base):
    __tablename__ = 'girl'
    id = Column(Integer, primary_key=True)
    name = Column(String(255, u'utf8_unicode_ci'), nullable=False)

class Match(Base):
    __tablename__ = 'match'
    id = Column(Integer, primary_key=True)
    guy_id = Column(ForeignKey(u'guy.id'), nullable=False)
    girl_id = Column(ForeignKey(u'girl.id'), nullable=False)

    guy = relationship(u'Guy', backref = 'matches')
    girl = relationship(u'Girl', backref = 'matches')

class GuyHobbies(Base):
    __tablename__ = 'guy_hobbies'
    id = Column(Integer, primary_key=True)
    guy_id = Column(ForeignKey(u'guy.id'), nullable=False)
    hobby_id = Column(ForeignKey(u'hobby.id'), nullable=False)

    guy = relationship(u'Guy', backref = 'hobbies')
    hobby = relationship(u'Hobby', backref = 'guys')

class GirlHobbies(Base):
    __tablename__ = 'girl_hobbies'
    id = Column(Integer, primary_key=True)
    girl_id = Column(ForeignKey(u'girl.id'), nullable=False)
    hobby_id = Column(ForeignKey(u'hobby.id'), nullable=False)

    girl = relationship(u'Girl', backref = 'hobbies')
    hobby = relationship(u'Hobby', backref = 'girls')

我现在想在 GirlHobby 和 GuyHobby 之间建立一个连接,同时考虑通过 Hobby 和 Match 建立的关系。即,我正在做一个

matched_hobbies = session.query(GuyHobbies).join(GuyHobbies.guy).\
    join(GuyHobbies.hobby).join(Guy.matches).\
    join(Match.girl).outerjoin(Girl.hobbies).all()

但是,生成的查询缺少下面标记的部分。如何让 SQLAlchemy 添加此条件?

SELECT guy_hobbies.id AS guy_hobbies_id, guy_hobbies.guy_id AS guy_hobbies_guy_id, 
    guy_hobbies.hobby_id AS guy_hobbies_hobby_id 
FROM guy_hobbies INNER JOIN guy ON guy.id = guy_hobbies.guy_id 
    INNER JOIN hobby ON hobby.id = guy_hobbies.hobby_id 
    INNER JOIN `match` ON guy.id = `match`.guy_id 
    INNER JOIN girl ON girl.id = `match`.girl_id 
    LEFT OUTER JOIN girl_hobbies ON girl.id = girl_hobbies.girl_id 
        #MISSING: AND hobby.id = girl_hobbies.hobby_id

补充:我尝试使用

在 GuyHobbies 和 GirlHobbies 之间建立直接关系
girl_hobbies = relationship('GirlHobbies', 
    primaryjoin ="and_(GuyHobbies.guy_id == Guy.id, 
    Match.guy_id == Guy.id ,Match.girl_id == Girl.id, 
    GirlHobbies.girl_id == Girl.id , 
    GirlHobbies.hobby_id == Hobby.id, 
    GuyHobbies.hobby_id == Hobby.id)")

但我收到一条颇具讽刺意味的错误消息:

Could not locate any simple equality expressions involving locally mapped foreign key columns for primary join condition 'guy_hobbies.guy_id = guy.id AND match.guy_id = guy.id AND match.girl_id = girl.id AND girl_hobbies.girl_id = girl.id AND girl_hobbies.hobby_id = hobby.id AND guy_hobbies.hobby_id = hobby.id' on relationship GuyHobbies.girl_hobbies.

它准确显示了我希望 SQLAlchemy 使用的连接条件...

【问题讨论】:

【参考方案1】:

感谢 SQLAlchemy IRC 频道的@inklesspen - 必须直接在连接中指定连接条件,即

matched_hobbies = session.query(GuyHobbies).join(GuyHobbies.guy).\
    join(GuyHobbies.hobby).join(Guy.matches).\
    join(Match.girl).\
    outerjoin(GirlHobbies, 
       and_(GirlHobbies.hobby_id == GuyHobbies.hobby_id,
            GirlHobbies.girl_id == Girl.id)).all()

【讨论】:

以上是关于与 SQLAlchemy 的分支关系的主要内容,如果未能解决你的问题,请参考以下文章

python SQLAlchemy

sqlalchemy 简单使用

Flask入门之SQLAlchemy配置与数据库连接

Python 开源项目大杂烩

flask-sqlalchemy 迁移数据(生成数据库表)与 查询数据

关系型数据库版本控制之(Flask SQLalchemy Alembic )