【问题标题】:Best Practice for SQLAlchemy and Self Referenced ObjectsSQLAlchemy 和自引用对象的最佳实践
【发布时间】:2016-04-18 22:09:52
【问题描述】:

我的模型中有一个自引用数据结构。可以这样形容

class Example(Base):
    __tablename__ = 'example'
    id = Column(Integer, primary_key=True)
    title = Column(String(255))
    parent_id = Column(Integer, ForeignKey('example.id'), nullable=True)
    parent = relationship("Example")

我想在会话中创建一个包含一个或多个孩子的父级。我的第一个方法是:

  1. 创建会话
  2. 更新/插入父级 (session.merge)
  3. 提交/关闭会话
  4. 创建新会话,加载父级,创建子级
  5. 设置child.parent_id = parent.id
  6. 提交/关闭会话

由于我想将整个过程作为事务处理,因此我想将其放在一个会话中。

如果我使用一个会话,我的问题:

设置child.parent = parent 会导致类型错误:ypeError: Incompatible collection type: None is not list-like。设置child.parent_id = parent.id 不起作用,因为尚未设置parent.id

【问题讨论】:

  • 不应该 parent_id 引用不同表的主键吗?不是自己表的键?
  • 这是一个自引用模型,根据我的理解,可能
  • 如果你提到你自己,我不相信你需要任何形式的relationship(尤其是如果你不在乎它是否可以为空)
  • 如果数据在父子关系中相关,则应通过外键链接以具有触发器,该触发器将在删除父级时清除所有子级。 Null 表示顶层的父级

标签: python sqlalchemy


【解决方案1】:

这里的 relationship() 作用相反,它代表一个子列表,而不是父列表。

它是这样工作的:

class Example(Base):
    __tablename__ = 'example'
    id = Column(Integer, primary_key=True)
    title = Column(String(255))
    parent_id = Column(Integer, ForeignKey('example.id'), nullable=True)
    children = relationship("Example")

然后您需要创建父记录并向其添加子记录:

    sql = sqldb.get_session()
    child1 = sqldb.system.Example()
    child1.title = 'child-test'
    sql.add(child1)

    child2 = sqldb.system.Example()
    child2.title = 'child-test2'
    sql.add(child2)

    parent = sqldb.system.Example()
    parent.title = 'parent-test'
    parent.children = [child1, child2]
    sql.add(parent)

    sql.commit()

【讨论】:

  • 谢谢。我应该更好地阅读文档。我假设我可以像在 Django 中一样使用它
【解决方案2】:
class Example(Base):
    __tablename__ = 'example'
    id = Column(Integer, primary_key=True)
    title = Column(String(255))
    parent_id = Column(Integer, nullable=True)

试试这样,因为你只使用一张表,你不需要和自己建立关系。

你应该可以做类似的事情

ex1 = Example(1,'test')
session.add(ex1)
ex2 = Example(2,'test2',1)
# or ex2 = Example(2,'test2',ex1.id)
session.add(ex2)

应该工作

如果您坚持要为各种触发器建立关系(出于灵活性考虑,我通常更愿意让程序员负责,而不是数据库),请使用remote_side

class Example(Base):
    __tablename__ = "example"
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.Text, unique=True)
    parent_id = db.Column(Integer,db.ForeignKey('example.id'))
    children= relationship("Example",
                backref=backref('parent', remote_side=[id])
            )

这将创建一个邻接列表关系 有关这方面的更多信息,请参阅。

http://docs.sqlalchemy.org/en/improve_toc/orm/self_referential.html#adjacency-list-relationships

【讨论】:

  • 在我看来,忽略关系不应被视为最佳实践,因为您正在失去在 DBMS 中拥有触发器或约束的能力。您的解决方案还假设我在所有交互中跟踪主键,这应该由 DBMS 完成
  • 我已经更新了我的答案。通常,我将您引用的此类约束留给与数据库通信的各个应用程序。例如,我有 3 个应用程序,使用一个数据库,但它们的用例都有不同的约束
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-23
  • 1970-01-01
  • 2011-07-12
  • 2011-12-27
  • 2021-01-18
  • 2014-01-28
相关资源
最近更新 更多