Python/SqlAlchemy 三方循环依赖

2 投票
1 回答
1325 浏览
提问于 2025-04-18 11:53

我在创建三个表之间的关系时遇到了问题。当我运行代码创建表时,出现了循环依赖的错误。

我尝试根据类似问题的回答,调整了一下 use_alterpost_update,但还是没能解决这个问题。

简单来说,一个地图有一组位置,一个角色有一组地图,但这个角色也位于这些地图中的某个位置。此外,一个地图还可以和其他地图有父子关系。

class Character(Base):
    __tablename__ = 'character'

    ID = Column(Integer, primary_key=True)
    name = Column(Unicode(255), nullable=False)
    classID = Column(Integer, nullable=False)
    created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)

    profileID = Column(Integer, ForeignKey('profile.ID'), nullable=False)
    locationID = Column(Integer, ForeignKey('location.ID'))

    location = relationship("Location")
    maps = relationship("Map", backref="owner", cascade="save-update, merge, delete, delete-orphan")

class Map(Base):
    __tablename__ = 'map'

    ID = Column(Integer, primary_key=True)
    name = Column(Unicode(255))
    maptypeID = Column(Integer, nullable=False)
    created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)

    parentID = Column(Integer, ForeignKey('map.ID'))
    ownerID = Column(Integer, ForeignKey('character.ID'))

    children = relationship("Map", backref=backref("parent", remote_side="Map.ID"))
    locations = relationship("Location", backref='map', cascade="save-update, merge, delete, delete-orphan")

class Location(Base):
    __tablename__ = 'location'

    ID = Column(Integer, primary_key=True)
    x = Column(Integer, nullable=False)
    y = Column(Integer, nullable=False)
    locationtypeID = Column(Integer, nullable=False)
    created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)

    mapID = Column(Integer, ForeignKey('map.ID'), nullable=False)

我该如何解决这个问题呢?

编辑(已解决):

经过进一步调整 use_alter,我通过改变位置类中的 mapID 定义解决了这个问题,原来的定义是:

mapID = Column(Integer, ForeignKey('map.ID'), nullable=False)

改为:

mapID = Column(Integer, ForeignKey('map.ID', use_alter=True, name="fk_location_map"), nullable=False)

针对打破循环依赖的建议,我更希望在架构中正确表示关系和数据完整性。我宁愿花时间修复 ORM 问题或更换 ORM,而不是为了适应 ORM 的期望而随意修改架构。

在这个特定情况下,我想不出更简洁优雅的方式来表示我需要架构为应用程序展示的所有信息(从最基本的意义上来说)。

顺便提一下:在使用其他语言/框架/ORM 时,这种问题通常会被 ORM 自动处理。例如,在 .NET E/F 中,我相信外键约束通常是在所有表创建语句之后添加和激活的。

1 个回答

0

你可能可以用alembic来解决你在数据库结构中遇到的循环依赖问题,但我不打算帮你解决这个。相反,我强烈建议你去打破这个循环依赖

如果我理解你的数据库结构没错,你的模型中有代表房间或建筑的部分叫做Location。而玩家和怪物在你的结构中被称为Character,每个Character在任何时候都必须处于一个特定的location。此外,还有一些Map物品,它们总是和某个特定的location有关,并且总是属于一个特定的character的背包里。

所以我会这样描述这个结构:

class Location:
    id = Column(Integer, primary_key=True)

class Character:
    id = Column(Integer, primary_key=True)
    location_id = Column(ForeignKey(Location.id))

    location = relationship(Location, backref="characters")

class Map:
    id = Column(Integer, primary_key=True)
    location_id = Column(ForeignKey(Location.id))
    owner_id = Column(ForeignKey(Character.id))

    location = relationship(Location, backref="maps")
    owner = relationship(Character, backref="inventory")

撰写回答