2012-06-07 38 views
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) 

皱纹是观察自己可以有各种不同的时代。当我尝试创建ObservationTime之间的一个一对多的关系,我得到一个循环依赖错误:

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中做错了什么?我对这一切都很陌生,所以它可能是上述任何一种。任何帮助你可以给予非常感激。

P.S.我试着做这里推荐的东西:Trying to avoid a circular reference但是我做错了,或者它没有解决我的特殊问题。

回答

4

其他有关您的用例重新审议的答案很有价值,您应该考虑这些问题。但是,就SQLAlchemy而言,由多个FK引起的循环依赖问题由use_alter/post_update组合解决,文档编号为http://docs.sqlalchemy.org/en/rel_0_7/orm/relationships.html#rows-that-point-to-themselves-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:[email protected]/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() 
+0

这太棒了。还有哪些地方可以从该软件的创建者那里获得关于软件包问题的详细的个性化答案?谢谢迈克! – IanVS

2

观察与状态之间有多对一的关系。所以一个国家可以有很多观察,每个观察都有一个国家。

您在国家和时间之间也有多对一的关系。所以一个时间可以有很多国家,每个国家都有一个时间。

你是正确的,因为问题是引用回到时代的观察。你迫使每个时间都有一个观察,而观察又必须有一个状态,而状态又必须有一个时间(然后循环会一直重复)。

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

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

+0

谢谢。我的意图是观察与国家之间,以及国家与时代之间的一对一。如果我编写错了,请帮我解决。我认为'uselist = True'是我如何使它成为一对一的。我希望Observations和Times之间有一对一的关系,这意味着Observation有许多不同的时代。 – IanVS

+0

@IanVS - 这是链接到sqlalchemy中的关系 http://docs.sqlalchemy.org/en/rel_0_7/orm/relationships.html 我自己并没有使用它,所以我不确定它是如何工作的,但是这对于如何建立不同类型的关系是一个很好的指导。 – Caleb

+0

@IanVS - 话虽如此,我认为你用你描述的方式来建立关系仍然存在问题。 您仍然通过以两种不同方式将观察连接到时间来创建一个圆。你要么不要让国家依赖泰晤士报,要么不要让观察依赖于泰晤士报,或者不要使观察依赖于国家。 我不知道这些对象实际上在你的应用程序中意味着什么,所以我不知道它们是如何相关的。但我不明白一个对象如何与一个“时间”相关,但也有许多“时间”是它自己的。 – Caleb

0

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

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

+0

感谢您的帮助。我想我看不到的东西(也许我错过了),是我如何拥有一个与时间相关的观察实例(将其称为观察时间)以及与不同时间(一个状态时间)相关的状态。我简化了我的例子,但实际上我可能会有一些“预测”状态,并且有将来的时间。所以一个观察将有一个观察时间,并且每个状态都有自己的状态时间。上面的设计是否支持这个? – IanVS

+0

@IanVS:我同意van的观点,即你的模型中的问题似乎在命名中表现出来。时间不是一个国家,也不是一个国家;一个国家是这样的情况,而不是这种情况,所以它没有时间。为什么你没有实体时间和状态相关的发生链接到两者? – reinierpost

+0

@reinierpost:一个国家如果没有一个国家有效的关联时间是毫无价值的,那么为什么你会说一个国家没有时间?也许这从我的描述不清楚。在我的情况下,“状态”是特定时间的位置,速度和加速度。以这种方式思考,也许为'state'添加一个'valid_time'列并让'time'表只与'observation'相关更有意义。 – IanVS