Open zopyx opened 2 years ago
I can confirm. Here is a simplified version for testing (compatible with Python 3.7+):
from typing import List, Optional
from sqlmodel import Field, SQLModel, create_engine, Session, Relationship
class Foo(SQLModel, table=True):
id: Optional[int] = Field(primary_key=True)
bars: List["Bar"] = Relationship(back_populates="foo")
class Bar(SQLModel, table=True):
__mapper_args__ = {
"polymorphic_on": "type",
"polymorphic_identity": "x",
}
id: Optional[int] = Field(primary_key=True)
type: str = Field(default="x")
foo_id: Optional[int] = Field(foreign_key="foo.id")
foo: Optional[Foo] = Relationship(back_populates="bars")
class BarY(Bar, table=True):
__mapper_args__ = {
"polymorphic_identity": "y",
}
class BarZ(Bar, table=True):
__mapper_args__ = {
"polymorphic_identity": "z",
}
db_url = f"sqlite:///:memory:"
engine = create_engine(db_url, echo=True)
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
foo = Foo()
foo.bars.extend([BarY(), Bar()])
session.add(foo)
session.commit()
Interestingly, you get a different error, if you instead just try to instantiate a BarY
object:
...
with Session(engine) as session:
bar = BarY()
session.add(bar)
session.commit()
I tried debugging the SQLModelMetaclass.__new__
method, to see if the __mapper_args__
end up on the resulting class, and it seems like they are. I checked this line here:
new_cls = super().__new__(cls, name, bases, dict_used, **config_kwargs)
When setting up Bar
for example, the new_cls.__dict__
contains the key value pair:
__mapper_args__: {'polymorphic_on': 'type', 'polymorphic_identity': 'x'}
So that seems not to be the issue.
This is all I got so far.
Someone helped me out with a slightly modified version that works for me:
from typing import Optional
import uuid
from typing import List
from sqlmodel import Field, SQLModel, create_engine, Session, Relationship
from uuid import UUID
from sqlalchemy.orm import registry
mapper_registry = registry()
class InvoiceRequest(SQLModel, table=True):
__tablename__ = "invoice_requests"
id: UUID = Field(default_factory=uuid.uuid4, primary_key=True)
product: str | None = None
request_type: str | None = None
invoices : List["Invoice"] = Relationship(back_populates="invoice_request")
def add_invoices(self):
self.invoices.append(InvoiceReversal())
self.invoices.append(InvoiceCorrection())
class Invoice(SQLModel, table=True):
# __tablename__ = "invoices"
id: UUID = Field(default_factory=uuid.uuid4, primary_key=True)
invoice_type: str = Field(default="regular")
invoice_request_id: UUID | None = Field(default=None, foreign_key="invoice_requests.id")
invoice_request: InvoiceRequest = Relationship(back_populates="invoices")
__mapper_args__ = {
"polymorphic_on": 'invoice_type',
"polymorphic_identity": "regular",
}
@mapper_registry.mapped
class InvoiceReversal(Invoice, table=True):
invoice_types: str = Field(default="regular")
__mapper_args__ = {
"polymorphic_identity": "reversal",
"inherit_condition": invoice_types == Invoice.invoice_type
}
@mapper_registry.mapped
class InvoiceCorrection(Invoice, table=True):
invoice_types: str = Field(default="correction")
__mapper_args__ = {
"polymorphic_identity": "correction",
"inherit_condition": invoice_types == Invoice.invoice_type
}
I tried this workaround with @mapper_registry.mapped
but I get "sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedColumn) column xxx does not exist".
With "inherit_condition" SQLAlchemy maked the correct join but thinks all columns are in the child table. There also a warning on start up, "SAWarning: Implicitly combining column parent.id with column child.id under attribute 'id'. Please configure one or more attributes for these same-named columns explicitly."
Any idea how to make this work?
Hello~, the current version is sqlmodel 0.0.19. Does anyone have idea about how to solve this?
@mapper_registry.mapped
not work for me
First Check
Commit to Help
Example Code
Description
I have this PoC-style code that would model a 1:n relationship between one
InvoiceRequest
and multipleInvoices
.There are several invoice types
ÃŒnvoiceReversal
andInvoiceCorrection
that are modelled using the inheritance fromInvoice
class and polymorphic identity.However. the derived classes are obviously not mapped:
sqlalchemy.orm.exc.UnmappedClassError: Class '__main__.InvoiceReversal' is not mapped
I could not find a solution how to resolve this.
Operating System
Linux
Operating System Details
Linux
SQLModel Version
SQLAlchemy==1.4.40
Python Version
3.10
Additional Context