[英]SQLAlchemy Single Table Inheritance with polymorphic_identity lacks WHERE
[英]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.