【问题标题】:Does Sqlalchemy really have One to one relationshipsSqlalchemy 真的有一对一的关系吗
【发布时间】:2017-03-10 19:13:57
【问题描述】:

我有以下符号。警报可以有状态更改并且只有一个。 状态更改只能有一个警报。状态变化可能有一个原因,也可能是许多状态变化 我尝试了以下架构

class Alert(BaseDb):
    __tablename__ = 'alerts'
    __table_args__ = (
        PrimaryKeyConstraint('id', name='pk_alerts'),
    )

    id = Column(Integer)
    alert_text = Column(Text)

class AlertStateChange(BaseDb):
    __tablename__ = 'alert_state_change'
    __table_args__ = (
        PrimaryKeyConstraint('id', name='pk_alert_state_change'),
        ForeignKeyConstraint(
            ['reason_id'],
            ['reasons.id'],
            name='fk_alert_status_change_reason_id__reasons'
        ),

        ForeignKeyConstraint(
            ['alert_id'],
            ['alerts.id'],
            name='fk_alert_status_change_alert_id__alert'
        ),


    )

    id = Column(Integer)
    reason_id = Column(Integer)
    alert_id = Column(Integer)
    reason = relationship('Reason', backref='status')
    alert = relationship('Alert',
                         backref=backref('status', uselist=False))
    status = Column(Text)

但是 sqlalchemy 允许我为相同的警报(相同的 alert_id)添加到 AlertStateChange 对象。它通常使用新的 id 提交。将两个 AlertStatusChange 对象放在数据库中的同一警报中尝试以下操作后

alert.status

给我以下警告

SAWarning: Multiple rows returned with uselist=False for lazily-loaded attribute 'Alert.status' % self.parent_property)

并且返回的对象是添加的第一个 AlertStateChange 对象。第二个在数据库中但被忽略了。不应该引发异常吗?这不是真正的 OneToOne 关系。我应该将 alert_id 添加为主键或唯一值对吗?

【问题讨论】:

  • 你是正确的,你应该在数据库级别强制执行一对一的方面,或者使用警报 id 作为主键/外键,或者对其具有唯一约束,如果你想确定。
  • 请注意,SQLA 关系是一个 ORM 概念,它只是映射到数据库中的模式,或者这就是我从docs.sqlalchemy.org/en/latest/orm/tutorial.html 中理解的方式

标签: python sqlalchemy one-to-one


【解决方案1】:

执行以下操作:

  • 在关系上设置uselist=False
  • 在子unique=True 中设置引用列
  • 您也可以在孩子上设置nullable=False
  • 您可以添加到父自定义__init__ 以实现严格的一对一

现在它可以工作了。

class Parent(Base):
    __tablename__ = 'parents'

    id = Column(Integer, primary_key=True)
    Child = relationship("Child", uselist=False, backref="Parent")

    def __init__(self,**kwargs):
        if 'Child' not in kwargs:
            raise RuntimeError('Need value')
        ...

class Child(Base):
    __tablename__ = 'childs'

    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('parents.id'), unique=True)    

Base.metadata.create_all(engine)
session = Session(bind=engine)

ch1 = Child(Parent=Parent())
session.add(ch1)

p1 = Parent(Child=Child())
session.add(p1)

session.commit()

for row in session.query(Parent):
    print row.Child.id
for row in session.query(Child):
    print row.Parent.id

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-01
    • 1970-01-01
    • 2019-12-06
    • 1970-01-01
    • 2017-02-06
    • 1970-01-01
    • 1970-01-01
    • 2016-06-15
    相关资源
    最近更新 更多