examples.inheritance.single — SQLAlchemy 2.0 Documentation

Release: 2.0.50 | Release Date: May 24, 2026

# SQLAlchemy 2.0 Documentation

# Source code for examples.inheritance.single

    """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()
        )