簡體   English   中英

sqlalchemy 中的單表 inheritance 的多態引用

[英]Polymorphic reference with single table inheritance in sqlalchemy

我是 sqlalchemy 的新手,並試圖弄清楚這應該如何工作。

我有兩個對象。 在 MWE 中(顯然它們具有比這更多的屬性和方法):

class Block:
    parts: Union[Block, Line]
    title: str

class Line:
    content: str
    title: str

我正在嘗試將這些保留在 sql 和 sqlalchemy 中,但我不知道如何保留parts關系。 這是我的嘗試,但它沒有創建任何引用(child_id 列始終為空):

from sqlalchemy import Column, ForeignKey, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, relationship

Base = declarative_base()


class Things(Base):
    __tablename__ = "things"
    id = Column(Integer, primary_key=True)
    type = Column(String)
    __mapper_args__ = {"polymorphic_on": type, "polymorphic_identity": "thing"}


class Assoc(Base):
    __tablename__ = "assoc"
    parent_id = Column(Integer, ForeignKey("things.id"), primary_key=True)
    child_id = Column(Integer, ForeignKey("things.id"), primary_key=True, nullable=True)


class Block(Things):
    __mapper_args__ = {"polymorphic_identity": "block"}
    title = Column(String)
    parts = relationship("Things", secondary="assoc", foreign_keys=[Assoc.parent_id])


class Line(Things):
    content = Column(String, index=True)
    __mapper_args__ = {"polymorphic_identity": "line"}


engine = create_engine("sqlite://", echo=False)
Base.metadata.create_all(engine)

session = Session(engine)

b = Block(title="block1", parts=[Block(title="subBlock 1"), Line(content="line1")])
l = Line(content="line2")
b.parts
b.parts.append(l)
b.parts
session.add(b)
session.commit()

session = Session(engine)

b = Block(title="block1", parts=[Block(title="subBlock 1"), Line(content="line1")])
l = Line(content="line2")
b.parts.append(l)
print("Before commit block1 parts:", [type(x).__name__ for x in b.parts])
print("Before commit block1 subBlock:", [x.title for x in b.parts if type(x) is Block])
session.add(b)
session.commit()

for i in session.query(Block):
    print(f"obj {i.title} in db parts:", [x.title for x in i.parts])

for i in session.query(Line):
    print("line in db:", i.content)
for i in session.query(Assoc):
    print(i.parent_id, i.child_id)

這產生:

Before commit block1 parts: ['Block', 'Line', 'Line']
Before commit block1 subBlock: ['subBlock 1']
obj block1 in db parts: []
obj subBlock 1 in db parts: ['subBlock 1']
line in db: line1
line in db: line2
2 None
3 None
4 None

我究竟做錯了什么? 如何引用parts中的線或塊

(我並不真正關心使用單表 inheritance,只是看起來更容易以這種方式引用“父表中的任何 object”。)

要建立多對多關系,您有兩種選擇:要么創建連接兩個表的關聯 object,要么創建一個附加的“事物”表,作為兩個表之間的鏈接。 我更喜歡后者,因為只有這種選擇才能添加額外的列來存儲可能適合“事物”的信息,而不是簡單的“塊”或“線”。 使用“ForeignKey”列類型來回引用,您需要創建一個“Relationship”屬性,以便您可以在 object 上執行“back_populates”,然后您應該對它有一個引用。

如果您擔心零件的順序,那么我建議您使用“sort_order”列(或類似名稱)來存儲該信息。 這幾乎意味着您必須使用“事物”表,因為那是您存儲此類事物的地方。

好吧,我想我已經想通了。 我很感激對這種方法的批評:我沒有使用 sqlalchemy 的經驗。

MWE:

from sqlalchemy import Column, ForeignKey, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, relationship

Base = declarative_base()


class Things(Base):
    __tablename__ = "things"
    id = Column(Integer, primary_key=True)
    type = Column(String)
    __mapper_args__ = {"polymorphic_on": type, "polymorphic_identity": "thing"}


class Assoc(Base):
    __tablename__ = "assoc"
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey("things.id"))
    child_id = Column(Integer, ForeignKey("things.id"))


class Block(Things):
    __mapper_args__ = {"polymorphic_identity": "block"}
    title = Column(String)
    parts = relationship(
        "Things",
        secondary="assoc",
        primaryjoin=Things.id == Assoc.parent_id,
        secondaryjoin=Things.id == Assoc.child_id,
    )

    def __repr__(self):
        return f"Block {self.title} with parts {[repr(x) for x in self.parts]}"


class Line(Things):
    content = Column(String, index=True)
    __mapper_args__ = {"polymorphic_identity": "line"}

    def __repr__(self):
        return f"Line with content {self.content}"


engine = create_engine("sqlite://", echo=False)
Base.metadata.create_all(engine)

session = Session(engine)


b = Block(title="block1", parts=[Block(title="subBlock 1"), Line(content="line1")])
l = Line(content="line2")
b.parts.append(l)
print("Pre commit")
print(b)
session.add(b)
c = Block(title="block2", parts=[Line(content="line3")])
print(c)
session.add(c)

session.commit()

print("\nThings:")
for i, obj in enumerate(session.query(Things)):
    print(f"{i} :", obj)

print("\nBlocks:")
for i, obj in enumerate(session.query(Block)):
    print(f"{i} :", obj)

print("\nAssoc:")
for i in session.query(Assoc):
    print(i.parent_id, i.child_id)

這產生:

Pre commit
Block block1 with parts ['Block subBlock 1 with parts []', 'Line with content line1', 'Line with content line2']
Block block2 with parts ['Line with content line3']

Things:
0 : Block block1 with parts ['Block subBlock 1 with parts []', 'Line with content line1', 'Line with content line2']
1 : Block subBlock 1 with parts []
2 : Line with content line1
3 : Line with content line2
4 : Block block2 with parts ['Line with content line3']
5 : Line with content line3

Blocks:
0 : Block block1 with parts ['Block subBlock 1 with parts []', 'Line with content line1', 'Line with content line2']
1 : Block subBlock 1 with parts []
2 : Block block2 with parts ['Line with content line3']

Assoc:
5 6
1 2
1 3
1 4

** 編輯:以前的錯誤是一個愚蠢的錯字。 今天是腦筋急轉彎的一天。 **

暫無
暫無

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

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