Python SQLAlchemy声明性语法中与抽象基的多对多多态关系

Python SQLAlchemy声明性语法中与抽象基的多对多多态关系,python,inheritance,sqlalchemy,many-to-many,declarative,Python,Inheritance,Sqlalchemy,Many To Many,Declarative,我正在创建一个具有多对多关系的SQLAlchemy声明性模型,其中一个表是具体的,另一个是具有抽象基的多态表 用例:我正在发送包含多个组件(组件1、组件2)的消息,每个组件都有非常不同的属性集(因此每个组件都有自己的数据库表)。一个组件可以在多个不同的消息中发送 我希望在消息类和名为Component的抽象父类之间有一个M:N关系,但是数据库不应该包含任何抽象的“Component”表,只包含具体子类的表(“component1”、“component2”等) 我尝试将(请参阅本章最后/第三段代

我正在创建一个具有多对多关系的SQLAlchemy声明性模型,其中一个表是具体的,另一个是具有抽象基的多态表

用例:我正在发送包含多个组件(组件1、组件2)的消息,每个组件都有非常不同的属性集(因此每个组件都有自己的数据库表)。一个组件可以在多个不同的消息中发送

我希望在消息类和名为Component的抽象父类之间有一个M:N关系,但是数据库不应该包含任何抽象的“Component”表,只包含具体子类的表(“component1”、“component2”等)

我尝试将(请参阅本章最后/第三段代码片段)与合并到以下代码中,但未能找到抽象基类的表名:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base, AbstractConcreteBase

BaseModel = declarative_base()

association = Table("message_component_association", BaseModel.metadata,
    Column("message_id", Integer, ForeignKey("message.id")),
    Column("component_id", Integer, ForeignKey("component.id")))  # Fails to reference the Component class

class Message(BaseModel):
    __tablename__ = "message"
    id = Column(Integer, primary_key=True)
    components = relationship("Component", secondary=association, back_populates="messages")

class Component(AbstractConcreteBase, BaseModel):
    __mapper_args__ = dict(polymorphic_identity="component", concrete=False)  # This seems to be ignored

class Component1(Component):
    __tablename__ = "component1"
    __mapper_args__ = dict(polymorphic_identity="component1", concrete=True)
    id = Column(Integer, primary_key=True)
    messages = relationship("Message", secondary=association, back_populates="components")

engine = create_engine("sqlite://")
BaseModel.metadata.create_all(engine)
例外情况是:

sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'message_component_association.component_id' could not find table 'component' with which to generate a foreign key to target column 'id'
为什么组件类的映射参数被忽略,并且无法通过提供的多态标识找到该类

编辑:我意识到我可以使用联接表继承(grr,我不能发布超过2个链接),它用显式鉴别器替换声明性帮助器mixin来获得多态的M:N关系-但是,基类仍然需要自己的数据库表

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

BaseModel = declarative_base()

association = Table("message_component_association", BaseModel.metadata,
Column("message_id", Integer, ForeignKey("message.id")),
Column("component_id", Integer, ForeignKey("component.id")))

class Message(BaseModel):
    __tablename__ = "message"
    id = Column(Integer, primary_key=True)
    components = relationship("Component", secondary=association, back_populates="messages")

class Component(BaseModel):  # Declarative mixin removed
    __tablename__ = "component"  # Requires a real DB table despite being abstract
    __mapper_args__ = dict(polymorphic_identity="component", polymorphic_on="type")  # Apply the discriminator
    id = Column(Integer, primary_key=True)
    type = Column(String(32))  # Explicit discriminator
    messages = relationship("Message", secondary=association, back_populates="components")

class Component1(Component):
    __tablename__ = "component1"
    __mapper_args__ = dict(polymorphic_identity="component1")
    id = Column(Integer, ForeignKey("component.id"), primary_key=True)  # Shares the primary key sequence with the parent and with all other child classes
    messages = relationship("Message", secondary=association, back_populates="components")

engine = create_engine("sqlite://", echo=True)
BaseModel.metadata.create_all(engine)
session = Session(engine)

component_1 = Component1(id=1)
session.commit()
到目前为止,该代码似乎仍能正常工作,但它抱怨flush出现问题。只要我不手动写入“组件表”,忽略警告是否安全?或者有更好的方法吗

SAWarning: Warning: relationship 'messages' on mapper 'Mapper|Component1|component1' supersedes the same relationship on inherited mapper 'Mapper|Component|component'; this can cause dependency issues during flush

解决方案:删除除消息类中的关系之外的所有关系,并用backref替换back_填充的关系。Backref将动态创建相反的方向,映射器将不会看到覆盖的关系。此外,抽象祖先上的多态性_标识也不是必需的

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

BaseModel = declarative_base()

association = Table("message_component_association", BaseModel.metadata,
Column("message_id", Integer, ForeignKey("message.id")),
Column("component_id", Integer, ForeignKey("component.id")))

class Message(BaseModel):
    __tablename__ = "message"
    id = Column(Integer, primary_key=True)
    components = relationship("Component", secondary=association, backref="messages")  # backref instead of back_populates

class Component(BaseModel):
    __tablename__ = "component"
    __mapper_args__ = dict(polymorphic_on="type")  # Polymorphic identity removed
    id = Column(Integer, primary_key=True)
    type = Column(String(32))

    # relationship removed

class Component1(Component):
    __tablename__ = "component1"
    __mapper_args__ = dict(polymorphic_identity="component1")
    id = Column(Integer, ForeignKey("component.id"), primary_key=True)

    # relationship removed

engine = create_engine("sqlite://", echo=True)
BaseModel.metadata.create_all(engine)
session = Session(engine)

component_1 = Component1(id=1)
session.commit()

链接到联接表继承: