SQLAlchemy polymorphic many-to-many relation with abstract base in declarative syntax
<p>I am creating a SQLAlchemy declarative model with many-to-many relation where one of the tables is concrete and another is polymorphic with abstract base.</p>

<p><strong>Use case:</strong> I am sending messages which contain multiple components (component1, component2) and each component has very different set of attributes (so each of the components has own database table). A component can be sent in multiple different messages.</p>

<p>I would like to have a M:N relation between the Message class and an abstract parent class called Component - but <strong>the database shouldn't contain any abstract "component" table</strong>, only tables for concrete children ("component1", "component2" etc.).</p>

<p>I tried to combine <a href="http://docs.sqlalchemy.org/en/rel_1_0/orm/inheritance.html#using-the-declarative-helper-classes" rel="nofollow">abstract 1:N relation using AbstractConcreteBase</a> (see the last/3rd code snippet in this chapter) with <a href="http://docs.sqlalchemy.org/en/latest/orm/basic_relationships.html#many-to-many" rel="nofollow">regular M:N relation</a> into the following code which fails to find the name of table for abstract base class:</p>

<pre><code>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)
</code></pre>

<p>The exception says:</p>

<pre><code>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'
</code></pre>

<p>Why are the <strong>mapper_args</strong> of Component class ignored and the class cannot be found via provided polymorphic_identity?</p>

<p><strong>EDIT:</strong> I've realized I can use Joined Table Inheritance (grr, I cannot post more than 2 links) which replaces the declarative helper mixin by explicit discriminator to get a polymorphic M:N relation - but still, <strong>the base class needs own database table</strong>.</p>

<pre><code>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()
</code></pre>

<p>The code seems to work so far but it <strong>complains about issues with flush</strong>. Is it safe to ignore the warning as long as I don't manually write to the "components table" -- or is there a better way?</p>

<pre><code>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
</code></pre>