Does Sqlalchemy really have One to one relationships

守給你的承諾、 提交于 2020-01-04 11:42:02

问题


I have the following scemantic. An alert can have a status change and only one. A status change can have only one alert. A status change can have one reason also a reason can be in maney status changes I tried the following schema

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)

but sqlalchemy lets me add to AlertStateChange objects for the same alert (same alert_id). It commits normally with a new id. After putting two AlertStatusChange objects for the same alert in the db trying the following

alert.status

gives me the following warning

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

and the object returned is the first AlertStateChange object added. The second is in the db but ignored. Shouldn't there be an exception raised? This isn't a real OneToOne relation. I should probably add the alert_id as a primary key or as a unique value correct?


回答1:


Perform these actions:

  • Set uselist=False on relationship
  • Set the referencing column in child unique=True
  • You can also set nullable=False on child
  • And you can add to Parent custom __init__ for strict one-to-one

Now it will work.

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


来源:https://stackoverflow.com/questions/42726070/does-sqlalchemy-really-have-one-to-one-relationships

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!