"""Single-table (table-per-hierarchy) inheritance example."""
from __future__ import annotations
from typing import Annotated
from sqlalchemy import create_engine
from sqlalchemy import ForeignKey
from sqlalchemy import FromClause
from sqlalchemy import or_
from sqlalchemy import select
from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session
from sqlalchemy.orm import with_polymorphic
intpk = Annotated[int, mapped_column(primary_key=True)]
str50 = Annotated[str, mapped_column(String(50))]
# columns that are local to subclasses must be nullable.
# we can still use a non-optional type, however
str50subclass = Annotated[str, mapped_column(String(50), nullable=True)]
class Base(DeclarativeBase):
pass
class Company(Base):
__tablename__ = "company"
id: Mapped[intpk]
name: Mapped[str50]
employees: Mapped[list[Person]] = relationship(
back_populates="company", cascade="all, delete-orphan"
)
def __repr__(self):
return f"Company {self.name}"
class Person(Base):
__tablename__ = "person"
__table__: FromClause
id: Mapped[intpk]
company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
name: Mapped[str50]
type: Mapped[str50]
company: Mapped[Company] = relationship(back_populates="employees")
__mapper_args__ = {
"polymorphic_identity": "person",
"polymorphic_on": "type",
}
def __repr__(self):
return f"Ordinary person {self.name}"
class Engineer(Person):
# illustrate a single-inh "conflicting" mapped_column declaration,
# where both subclasses want to share the same column that is nonetheless
# not "local" to the base class
@declared_attr
def status(cls) -> Mapped[str50]:
return Person.__table__.c.get(
"status", mapped_column(String(30)) # type: ignore
)
engineer_name: Mapped[str50subclass]
primary_language: Mapped[str50subclass]
__mapper_args__ = {"polymorphic_identity": "engineer"}
def __repr__(self):
return (
f"Engineer {self.name}, status {self.status}, "
f"engineer_name {self.engineer_name}, "
f"primary_language {self.primary_language}"
)
class Manager(Person):
manager_name: Mapped[str50subclass]
# illustrate a single-inh "conflicting" mapped_column declaration,
# where both subclasses want to share the same column that is nonetheless
# not "local" to the base class
@declared_attr
def status(cls) -> Mapped[str50]:
return Person.__table__.c.get(
"status", mapped_column(String(30)) # type: ignore
)
__mapper_args__ = {"polymorphic_identity": "manager"}
def __repr__(self):
return (
f"Manager {self.name}, status {self.status}, "
f"manager_name {self.manager_name}"
)
engine = create_engine("sqlite://", echo=True)
Base.metadata.create_all(engine)
with Session(engine) as session:
c = Company(
name="company1",
employees=[
Manager(
name="mr krabs",
status="AAB",
manager_name="manager1",
),
Engineer(
name="spongebob",
status="BBA",
engineer_name="engineer1",
primary_language="java",
),
Person(name="joesmith"),
Engineer(
name="patrick",
status="CGG",
engineer_name="engineer2",
primary_language="python",
),
Manager(name="jsmith", status="ABA", manager_name="manager2"),
],
)
session.add(c)
session.commit()
for e in c.employees:
print(e)
spongebob = session.scalars(
select(Person).filter_by(name="spongebob")
).one()
spongebob2 = session.scalars(
select(Engineer).filter_by(name="spongebob")
).one()
assert spongebob is spongebob2
spongebob2.engineer_name = "hes spongebob!"
session.commit()
# query using with_polymorphic.
eng_manager = with_polymorphic(Person, [Engineer, Manager])
print(
session.scalars(
select(eng_manager).filter(
or_(
eng_manager.Engineer.engineer_name == "engineer1",
eng_manager.Manager.manager_name == "manager2",
)
)
).all()
)
# illustrate join from Company.
print(
session.scalars(
select(Company)
.join(Company.employees.of_type(eng_manager))
.filter(
or_(
eng_manager.Engineer.engineer_name == "engineer1",
eng_manager.Manager.manager_name == "manager2",
)
)
).all()
)