簡體   English   中英

SQLAlchemy 繼承表之間的關系拋出主鍵錯誤

[英]SQLAlchemy relationship between inherited tables throws primary key error

我正在嘗試在 SQLAlchemy 中設置一個加入的 inheritance ,它工作正常。 我的模式設計需要兩個繼承表之間的一對多關系。 我的實際工作示例非常復雜,但我能夠通過 SQLAlchemy 和 inheritance 教程代碼重現該問題。

"""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。 兩者都繼承自一個公共資源 class(在現實生活中,公共基礎是必要的,它包含更多列)。 ParentResource 和 ChildResource 之間存在一對多的關系。 這些表在 sqlite 和 postgres 中正確創建,但是當我嘗試將一個父級和一個子級 object 添加到 session 時,出現以下錯誤:

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 約束。 我究竟做錯了什么?

above_c_level 所建議的,最小的解決方案是更改基本 class 中的主鍵列名稱。 我犯的錯誤是基礎 class 和子類具有被子類覆蓋的“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()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM