SQLAlchemy - 循环数据 - CircularDependencyError

时间:2014-03-11 06:17:22

标签: python sqlalchemy

我正在尝试使用SQLAlchemy而且我没有SQL或SQLAlchemy经验。

我试图表示的数据不是简单的分层树。

有两种数据类型:记录和目标。它们是如此相关:

  • 记录中有一个或多个目标。
  • 目标只有零个记录。
  • 目标可能会共享记录
  • 记录可以共享目标。

Base是SQLAlchemy的声明基础。

这是我尝试的代码,但是,我得到了CircularDependencyError,但数据是循环的。

代码:

class Record(Model, Base):
    """"""
    __tablename__ = "records"
    NAME          = Column(String, primary_key = True)
    TITLE         = Column(Text)
    COMPANY       = Column(Text)
    LOCATION      = Column(Text)
    DATE          = Column(Text)
    BLURB         = Column(Text)
    BULLET        = Column(Text)
    URL           = Column(Text)

    targets_id  = Column(Text, ForeignKey("targets.NAME"))
    targets = relationship("Target", back_populates = "records")

class Target(Model, Base):
    """"""
    __tablename__ = "targets"
    NAME          = Column(Text, primary_key = True)
    MODULE        = Column(Text)
    URL           = Column(Text)
    JOB_URL       = Column(Text)
    COMPANY       = Column(Text)

    records_id  = Column(String, ForeignKey("records.NAME"))
    records = relationship("Record", back_populates = "targets")

我看过http://docs.sqlalchemy.org/en/rel_0_9/orm/relationships.html#many-to-many,但这看起来有点像黑客。是否有另一种方法可以在SQLAlchemy中表示这种方法来保留这种循环关系?

修改

一些示例数据:

RA:
    NAME     = "RA"
    TITLE    = ...
    COMPANY  = ...
    LOCATION = ...
    DATE     = ...
    BLURB    = ...
    BULLET   = ...
    URL      = ...
    TARGETS  = [ TA, TD, ... ]

RB:
    NAME     = "RB"
    TITLE    = ...
    COMPANY  = ...
    LOCATION = ...
    DATE     = ...
    BLURB    = ...
    BULLET   = ...
    URL      = ...
    TARGETS  = [ TB, TD, ... ]

TA:
    NAME    = "TA"
    MODULE  = ...
    URL     = ...
    JOB_URL = ...
    COMPANY = ...
    RECORDS = [ RA, RB, ... ]

1 个答案:

答案 0 :(得分:0)

作为RedBaron mentioned,SQL需要一个循环数据的中间表。

当我使用声明性基础时,我想要一个一致的解决方案;与documentation中提供的不同。

文档将中间表称为关联对象。

使用这些对象的语法在主观上是丑陋的,特别是在附加相关记录时(但这是SQL自身限制的结果)。所以我使用了Association Proxy;但是文档中的示例让新手感到困惑。

此解决方案基于this answer中找到的解决方案。

代码:

from sqlalchemy.ext.associationproxy import association_proxy

class Record(Model, Base):
    """"""
    __tablename__ = "records"
    NAME          = Column(String, primary_key = True)
    TITLE         = Column(Text)
    COMPANY       = Column(Text)
    LOCATION      = Column(Text)
    DATE          = Column(Text)
    BLURB         = Column(Text)
    BULLET        = Column(Text)
    URL           = Column(Text)

    targets       = association_proxy("target_records", "target", creator = lambda target: Target_Record(target = target))

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, self.NAME or "Unamed")

class Target(Model, Base):
    """"""
    __tablename__ = "targets"
    NAME          = Column(Text, primary_key = True)
    MODULE        = Column(Text)
    URL           = Column(Text)
    JOB_URL       = Column(Text)
    COMPANY       = Column(Text)

    records       = association_proxy("target_records", "record", creator = lambda record: Target_Record(record = record))

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, self.NAME or "Unamed")

class Target_Record(Model, Base):
    __tablename__ = "target_records"
    target_id     = Column(Text  , ForeignKey("targets.NAME"), primary_key = True)
    record_id     = Column(String, ForeignKey("records.NAME"), primary_key = True)
    target        = relationship(Target, backref = backref("target_records"))
    record        = relationship(Record, backref = backref("target_records"))