You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用SQLAlchemy通过关联模型实现同一模型的一对一关系

解决SQLAlchemy同一模型一对一关联的问题

修正后的代码实现

from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, create_engine
from sqlalchemy.orm import relationship, Session
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Person(Base):
    __tablename__ = 'persons'

    id = Column(Integer, primary_key=True)
    
    # 分别定义作为关系双方的反向关联,uselist=False确保一对一
    relationship_as_person1 = relationship(
        'PersonRelationship',
        foreign_keys='PersonRelationship.person1_id',
        uselist=False,
        back_populates='person1'
    )
    relationship_as_person2 = relationship(
        'PersonRelationship',
        foreign_keys='PersonRelationship.person2_id',
        uselist=False,
        back_populates='person2'
    )

    # 快捷属性,直接获取关联的伴侣
    @property
    def partner(self):
        if self.relationship_as_person1:
            return self.relationship_as_person1.person2
        elif self.relationship_as_person2:
            return self.relationship_as_person2.person1
        return None

class PersonRelationship(Base):
    __tablename__ = 'person_relationships'

    id = Column(Integer, primary_key=True)
    person1_id = Column(Integer, ForeignKey('persons.id'))
    person2_id = Column(Integer, ForeignKey('persons.id'))
    relationship_type = Column(String(50))  # 存储关系类型,比如"伴侣"、"好友"等

    # 添加唯一约束,确保一个人只能出现在一个一对一关系中
    __table_args__ = (
        UniqueConstraint('person1_id', name='unique_person1'),
        UniqueConstraint('person2_id', name='unique_person2'),
    )

    # 关联到Person模型的对应反向关系
    person1 = relationship(
        'Person',
        foreign_keys=[person1_id],
        back_populates='relationship_as_person1'
    )
    person2 = relationship(
        'Person',
        foreign_keys=[person2_id],
        back_populates='relationship_as_person2'
    )

# 初始化数据库
engine = create_engine('sqlite:///the_database.db')
session = Session(engine)
Base.metadata.create_all(engine)

# 创建实例并关联
p1 = Person()
p2 = Person()
partner_relation = PersonRelationship(person1=p1, person2=p2, relationship_type='伴侣')

session.add_all([p1, p2, partner_relation])
session.commit()

# 测试获取关联对象
print(p1.partner.id)  # 输出p2的id
print(p2.partner.id)  # 输出p1的id

关键修正点

  • 拆分反向关联:原代码用单个partner_relationship同时映射两个外键的反向关系,SQLAlchemy无法区分来源,因此拆分为relationship_as_person1和relationship_as_person2,分别对应关联表中的两个外键。
  • 强制一对一:通过uselist=False将关系设置为一对一,而非默认的一对多。
  • 唯一性约束:在关联表中给person1_id和person2_id分别添加唯一约束,确保一个用户只能存在于一个一对一关系中。
  • 自动处理外键:创建关联对象时无需手动传入person1_id和person2_id,SQLAlchemy会在提交会话时自动填充生成的主键值。
  • 快捷属性封装:通过partner属性简化伴侣对象的获取,无需区分当前用户是关系中的哪一方。

内容的提问来源于stack exchange,提问作者Touten

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 19:12:16