【问题标题】:SQLAlchemy relationship between inherited tables throws primary key error继承表之间的 SQLAlchemy 关系引发主键错误
【发布时间】:2020-06-17 15:03:58
【问题描述】:

我正在尝试在 SQLAlchemy 中设置一个连接继承,它工作正常。我的架构设计需要两个继承表之间的一对多关系。我的实际工作示例非常复杂,但我能够使用 SQLAlchemy 加入继承教程代码重现该问题。

"""Joined-table (table-per-subclass) inheritance example."""

from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import ForeignKey
from sqlalchemy import inspect
from sqlalchemy import Integer
from sqlalchemy import or_
from sqlalchemy import String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session
from sqlalchemy.orm import with_polymorphic


Base = declarative_base()

class Resource(Base):
    __tablename__ = "resource"
    id = Column(Integer, primary_key=True)
    # Permissions and other common columns.
    # Left out for simplicity
    type = Column(String(50))

    __mapper_args__ = {
        "polymorphic_identity": "resource",
        "polymorphic_on": type,
    }

class ChildResource(Resource):
    __tablename__ = "child_resource"
    id = Column(ForeignKey("resource.id"), primary_key=True)
    name = Column(String(30))

    parent_id = Column(ForeignKey('parent_resource.id'), nullable=False)
    parent = relationship('ParentResource', back_populates='children', foreign_keys=parent_id)

    __mapper_args__ = {"polymorphic_identity": "child_resource"}

class ParentResource(Resource):
    __tablename__ = "parent_resource"
    id = Column(ForeignKey("resource.id"), primary_key=True)
    name = Column(String(30))

    children = relationship('ChildResource', back_populates='parent', foreign_keys='ChildResource.id')

    __mapper_args__ = {"polymorphic_identity": "parent_resource"}

if __name__ == "__main__":
    engine = create_engine("sqlite://", echo=True)
    Base.metadata.create_all(engine)

    session = Session(engine)

    res_child = ChildResource(
                name="My child shared resource",
            )

    res_parent = ParentResource(
                name="My parent shared resource"
            )

    res_parent.children.append(res_child)
    session.add(res_child)
    session.add(res_parent)

    session.commit()

所以我有一个 ParentResource 和一个 ChildResource。两者都继承自一个公共资源类(在现实生活中,公共基础是必要的,它包含更多列)。 ParentResource 和 ChildResource 之间存在一对多的关系。这些表是在 sqlite & postgres 中正确创建的,但是当我尝试向会话中添加一个父对象和一个子对象时,出现以下错误:

sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: resource.id
[SQL: INSERT INTO resource (id, type) VALUES (?, ?)]
[parameters: (1, 'child_resource')]
(Background on this error at: http://sqlalche.me/e/gkpj)

当我检查 SQLAlchemy 回显时,我看到以下内容。

2020-06-17 07:28:42,276 INFO sqlalchemy.engine.base.Engine ()
2020-06-17 07:28:42,276 INFO sqlalchemy.engine.base.Engine COMMIT
2020-06-17 07:28:42,280 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2020-06-17 07:28:42,280 INFO sqlalchemy.engine.base.Engine INSERT INTO resource (type) VALUES (?)
2020-06-17 07:28:42,280 INFO sqlalchemy.engine.base.Engine ('parent_resource',)
2020-06-17 07:28:42,281 INFO sqlalchemy.engine.base.Engine INSERT INTO parent_resource (id, name) VALUES (?, ?)
2020-06-17 07:28:42,281 INFO sqlalchemy.engine.base.Engine (1, 'My parent shared resource')
2020-06-17 07:28:42,281 INFO sqlalchemy.engine.base.Engine INSERT INTO resource (id, type) VALUES (?, ?)
2020-06-17 07:28:42,281 INFO sqlalchemy.engine.base.Engine (1, 'specific_resource_1')
2020-06-17 07:28:42,281 INFO sqlalchemy.engine.base.Engine ROLLBACK

看起来 res_child 和 res_parent 将获得相同的主键,这当然会破坏 pk 约束。 我做错了什么?

【问题讨论】:

  • 两个继承表中真的需要 ForeignKey("resource.id") 吗? IE。你能把 id 重构为 parent_id、resource_id 和 child_id 吗?这样你的表之间的关系会更清晰。
  • 感谢您的回答。我刚刚将所有 id 列重命名为相应的 resource_id、parent_id 和 child_id,现在它可以正常工作了。我只是想了解我的第一种方法有什么问题。顺便说一句,我在官方 SQLAlchemy example 中看到的 ForeignKey("person.id") 也被使用。这样做的目的是什么?

标签: python postgresql sqlalchemy flask-sqlalchemy


【解决方案1】:

正如 above_c_level 所建议的,最小的解决方案是更改基类中的主键列名称。我在基类和子类中都犯的错误具有被子类覆盖的“id”属性。您可以在下面找到工作代码示例。

"""Joined-table (table-per-subclass) inheritance example."""

from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import ForeignKey
from sqlalchemy import inspect
from sqlalchemy import Integer
from sqlalchemy import or_
from sqlalchemy import String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session
from sqlalchemy.orm import with_polymorphic


Base = declarative_base()

class Resource(Base):
    __tablename__ = "resource"
    resource_id = Column(Integer, primary_key=True)
    # Permissions and other common columns.
    # Left out for simplicity
    type = Column(String(50))

    __mapper_args__ = {
        "polymorphic_identity": "resource",
        "polymorphic_on": type,
    }

class ChildResource(Resource):
    __tablename__ = "child_resource"
    id = Column(ForeignKey("resource.resource_id"), primary_key=True)
    name = Column(String(30))

    parent_id = Column(ForeignKey('parent_resource.id'), nullable=False)
    parent = relationship('ParentResource', back_populates='children', foreign_keys=parent_id)

    __mapper_args__ = {"polymorphic_identity": "child_resource", "inherit_condition": id == Resource.resource_id}

class ParentResource(Resource):
    __tablename__ = "parent_resource"
    id = Column(ForeignKey("resource.resource_id"), primary_key=True)
    name = Column(String(30))

    children = relationship('ChildResource', back_populates='parent', foreign_keys='ChildResource.id')

    __mapper_args__ = {"polymorphic_identity": "parent_resource", "inherit_condition": id == Resource.resource_id}

if __name__ == "__main__":
    engine = create_engine("sqlite://", echo=True)
    Base.metadata.create_all(engine)

    session = Session(engine)

    res_child = ChildResource(
                name="My child shared resource",
            )

    res_parent = ParentResource(
                name="My parent shared resource"
            )

    res_parent.children.append(res_child)
    session.add(res_child)
    session.add(res_parent)

    session.commit()

【讨论】:

    猜你喜欢
    • 2012-10-06
    • 2010-10-24
    • 1970-01-01
    • 2017-04-05
    • 1970-01-01
    • 1970-01-01
    • 2014-11-09
    • 2023-03-17
    • 2012-07-11
    相关资源
    最近更新 更多