1

我正在使用 sqlalchemy 设计一个数据库来存放科学测试数据。我遇到了一个我似乎无法弄清楚的问题。

在我的测试数据中,每个Observation都有一个 State(位置、速度、加速度),一个State有一个关联Time的(状态应用的时间)。到目前为止,一切都很好。我制作了一个单独的表,Times因为我处理不同类型的时间,并且我想使用一个参考表来指示每个时间是什么类型的时间(状态时间,观察时间等)。而且我处理的时间类型可能会改变,所以我认为以这种方式标准化会让我在未来添加新的时间类型,因为它们只是参考表中的行。

到目前为止,这部分有效(使用声明式风格):

class Observation(Base):
    __tablename__ = 'tbl_observations'
    id = Column(Integer, primary_key=True)
    state_id = Column(Integer, ForeignKey('tbl_states.id'))
    state = relationship('State', uselist=False)

class State(Base):
    __tablename__ = 'tbl_states'
    id = Column(Integer, primary_key=True)
    time_id = Column(Integer, ForeignKey('tbl_times.id'))
    time = relationship('Time', uselist=False)

class Time(Base):
    __tablename__ = 'tbl_times'
    id = Column(Integer, primary_key=True)
    time_type_id = Column(Integer, ForeignKey('ref_tbl_time_types.id'))
    time_type = relationship('TimeType', uselist=False)
    time_value = Column(Float)

class TimeType(Base):
    __tablename__ = 'ref_tbl_time_types'
    id = Column(Integer, primary_key=True)
    desc = Column(String)

皱纹是观察本身可以有不同的时间。Observation当我尝试在and之间创建一对多关系Time时,出现循环依赖错误:

class Observation(Base):
    __tablename__ = 'tbl_observations'
    id = Column(Integer, primary_key=True)
    state_id = Column(Integer, ForeignKey('tbl_states.id'))
    state = relationship('State', uselist=False)

    # Added this line:
    times = relationship('Time')

class Time(Base):
    __tablename__ = 'tbl_times'
    id = Column(Integer, primary_key=True)
    time_type_id = Column(Integer, ForeignKey('ref_tbl_time_types.id'))
    time_type = relationship('TimeType', uselist=False)
    time_value = Column(Float)

    # Added this line:
    observation_id = Column(Integer, ForeignKey('tbl_observations.id'))

我猜这会中断,因为原始Observation-> State->Time链有一个引用到Observation.

有没有什么办法解决这一问题?我把我的设计搞砸了吗?我在 sqlalchemy 中做错了吗?我对这一切都很陌生,所以它可能是上述任何一种。您可以提供的任何帮助将不胜感激。

PS我尝试做这里推荐的事情:试图避免循环引用,但要么我做错了,要么它没有解决我的特定问题。

4

3 回答 3

4

这里关于重新考虑您的用例的其他答案很有价值,您应该考虑这些。然而,就 SQLAlchemy 而言,由于多个 FK 导致的循环依赖问题由 use_alter/post_update 组合解决,记录在http://docs.sqlalchemy.org/en/rel_0_7/orm/relationships.html#rows- that-point-to-themself-mutually-dependent-rows。这是使用它的模型:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base= declarative_base()

class Observation(Base):
    __tablename__ = 'tbl_observations'
    id = Column(Integer, primary_key=True)
    state_id = Column(Integer, ForeignKey('tbl_states.id'))
    state = relationship('State', uselist=False)

    times = relationship('Time')

class State(Base):
    __tablename__ = 'tbl_states'
    id = Column(Integer, primary_key=True)
    time_id = Column(Integer, ForeignKey('tbl_times.id'))

    # post_update is preferable on the many-to-one
    # only to reduce the number of UPDATE statements
    # versus it being on a one-to-many.
    # It can be on Observation.times just as easily.
    time = relationship('Time', post_update=True)

class Time(Base):
    __tablename__ = 'tbl_times'
    id = Column(Integer, primary_key=True)
    time_type_id = Column(Integer, ForeignKey('ref_tbl_time_types.id'))
    time_type = relationship('TimeType', uselist=False)
    time_value = Column(Float)

    observation_id = Column(Integer, ForeignKey('tbl_observations.id', 
                                    use_alter=True, name="fk_time_obs_id"))

class TimeType(Base):
    __tablename__ = 'ref_tbl_time_types'
    id = Column(Integer, primary_key=True)
    desc = Column(String)


e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
Base.metadata.drop_all(e)
Base.metadata.create_all(e)

s = Session(e)

tt1 = TimeType(desc="some time type")
t1, t2, t3, t4, t5 = Time(time_type=tt1, time_value=40), \
                Time(time_type=tt1, time_value=50), \
                Time(time_type=tt1, time_value=60),\
                Time(time_type=tt1, time_value=70),\
                Time(time_type=tt1, time_value=80)

s.add_all([
    Observation(state=State(time=t1), times=[t1, t2]),
    Observation(state=State(time=t2), times=[t1, t3, t4]),
    Observation(state=State(time=t2), times=[t2, t3, t4, t5]),
])

s.commit()
于 2012-06-12T23:01:27.700 回答
2

观察和状态之间存在多对一的关系。所以一个 State 可以有很多 Observation,每个 Observation 都有一个 State。

State 和 Times 之间也存在多对一的关系。所以一个Time可以有多个State,每个State都有一个Time。

您是正确的,因为问题是对时代观察的参考。您强制每个 Time 有一个 Observation,而后者又必须有一个 State,而 State 又必须有一个 Time(然后循环永远重复)。

要打破这一点,您需要弄清楚您在这些关系中实际试图描绘的内容。如果一个观察有一个状态,它有一个时间,那么观察有一个时间(你可以从状态中得到时间)。

所以你需要回答的真正问题是:说一个时间有一个观察是什么意思?您将如何在您的应用程序中使用它?

于 2012-06-07T20:11:43.943 回答
0

我想我没有完全了解您的对象模型中的模型名称以及它们与现实世界的对应关系。但我会尝试猜测。首先,我怀疑该模型Time(看起来相当基本且几乎没有逻辑)是否应该具有ForeignKey一些更高级别的模型类Observation。鉴于此,我认为您的模型不是一个n-1关系链,而是一种三元关系。所以我可以看到你的模型如下:

class Base(object):
    id = Column(Integer, primary_key=True)

class Observation(Base):
    __tablename__ = 'tbl_observations'

class ObservationInstance(Base):
    __tablename__ = 'tbl_observation_instances'
    observation_id = Column(Integer, ForeignKey('tbl_observations.id'))
    state_id = Column(Integer, ForeignKey('tbl_states.id'))
    time_id = Column(Integer, ForeignKey('tbl_times.id'))

    # relationships
    observation = relationship('Observation', backref="instances")
    state = relationship('State')
    time = relationship('Time')

class State(Base):
    __tablename__ = 'tbl_states'

class Time(Base):
    __tablename__ = 'tbl_times'
    time_type_id = Column(Integer, ForeignKey('ref_tbl_time_types.id'))
    time_type = relationship('TimeType', uselist=False)
    time_value = Column(Float)

class TimeType(Base):
    __tablename__ = 'ref_tbl_time_types'
    desc = Column(String)

希望这有任何意义,并且适合您尝试建模的真实世界。我假设您的模型代表某种(科学)实验。在这种情况下,我将重命名Observation -> ExperiementObservationInstance -> Observation.

于 2012-06-08T20:36:52.723 回答