Skip to content

Conversation

@cofin
Copy link
Member

@cofin cofin commented Nov 16, 2025

Summary

This PR completes the implementation of SQLAlchemy inheritance pattern support in CommonTableAttributes, enabling proper handling of Single Table Inheritance (STI), Joined Table Inheritance (JTI), and Concrete Table Inheritance (CTI).

Changes

Core Implementation (advanced_alchemy/base.py)

  1. Added __init_subclass__ hook (lines 209-277):

    • Detects STI children by checking if any parent has polymorphic_on in their __mapper_args__
    • Automatically sets cls.__tablename__ = None for STI children
    • Handles both explicit (with polymorphic_identity) and implicit (without) STI children
    • Properly distinguishes between base classes and child classes
  2. Enhanced @declared_attr.__tablename__() method (lines 281-386):

    • Returns None for explicitly set None values (set by __init_subclass__)
    • Provides fallback STI detection for cases where parent doesn't have explicit tablename
    • Generates snake_case table names for non-STI classes

Supported Patterns

  1. Single Table Inheritance (STI):

    • Child classes with polymorphic_identity share parent's table
    • Works with both auto-generated and explicit parent table names
    • Handles edge case of children without polymorphic_identity (with warning)
  2. Joined Table Inheritance (JTI):

    • Child classes with explicit __tablename__ create joined tables
    • Proper foreign key relationships maintained
  3. Concrete Table Inheritance (CTI):

    • Child classes with concrete=True create independent tables
    • No foreign keys to parent table

Related Issues

Supersedes PR #600

@github-actions
Copy link

Documentation preview will be available shortly at https://litestar-org.github.io/advanced-alchemy-docs-preview/611

- Modified CommonTableAttributes.__tablename__ to detect inheritance patterns
- Changed from @declared_attr.directive (was causing issues) back to .directive
- Returns None for STI children to use parent's table
- Returns generated/explicit names for JTI/CTI patterns
- Handles edge cases: mixins, abstract bases, multi-level hierarchies

Fixes automatic table name generation for SQLAlchemy inheritance:
- Single Table Inheritance (STI): Child uses parent table
- Joined Table Inheritance (JTI): Child has own table with FK
- Concrete Table Inheritance (CTI): Independent tables with concrete=True
The trailing underscore in `SyncServiceT_…` was being interpreted as
an RST reference target, causing the doc build to fail with
"Unknown target name: syncservicet".
@cofin cofin force-pushed the fix/sqlalchemy-inheritance-proper branch from 6ef1f53 to dc2b9d5 Compare December 15, 2025 17:31
@njz-cvm
Copy link

njz-cvm commented Jan 7, 2026

Would it be possible to add Polymorphism support for services and repositories? Currently, I've only been able to make Polymorphism work by creating a service for each subclass. Ideally it would work something like this:

AllAlerts = with_polymorphic(Alert, (UnexpectedAlert, ExpectedAlert))

class AlertService(SQLAlchemyAsyncRepositoryService[AllAlerts]):

    class Repo(SQLAlchemyAsyncRepository[AllAlerts]):
        model_type = AllAlerts

    repository_type = Repo

AllAlerts is an AliasedClass that contains information about the Polymorphic relation. Just like SQLAlchemy, Advanced Alchemy typing would hint that it's methods return the abstract class.

alert: Alert = await alert_service.create(data)

But in practice, it really returns the full Polymorphic identity. I'm not sure how much additional work would be required, but this would be a great boon for services that provide many polymorphic identities.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants