Question: I have a situation where I want to set up relationships between tables, mapped with the SQLAlchemy ORM layer, where these relationships have an extra join key. As far as I know, setting this up by hand requires embedding strings that are eval'd; I'm trying to figure out to what extent that can be avoided, or at least validated early (ideally by pyright or mypy, before runtime). Take the following schema, which doesn't yet have the extra join key added: from typing import List from uuid import UUID from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship from sqlalchemy.schema import ForeignKey from sqlalchemy.types import Uuid import sqlalchemy as sa class Base(DeclarativeBase): pass class User(Base): __tablename__ = 'user' id: Mapped[UUID] = mapped_column(Uuid(), primary_key=True) tenant_id: Mapped[UUID] = mapped_column(Uuid()) actions: Mapped[List["Action"]] = relationship("Action", back_populates="user", foreign_keys="Action.user_id") class Action(Base): __tablename__ = 'action' id: Mapped[UUID] = mapped_column(Uuid(), primary_key=True) tenant_id: Mapped[UUID] = mapped_column(Uuid()) user_id: Mapped[UUID] = mapped_column(Uuid(), ForeignKey("user.id")) user: Mapped["User"] = relationship("User", back_populates="actions", foreign_keys=[user_id]) Base.metadata.create_all(sa.create_engine('sqlite://', echo=True))  That's simple enough as long as we aren't trying to add belt-and-suspenders protection against relationship evaluations linking across tenants. As soon as we do, the relation declarations need to be strings any time there's need to refer to an as-yet-undeclared class: class User(Base): __tablename__ = "user" id: Mapped[UUID] = mapped_column(Uuid(), primary_key=True) tenant_id: Mapped[UUID] = mapped_column(Uuid()) actions: Mapped["Action"] = relationship("Action", back_populates="user", foreign_keys="Action.user_id", primaryjoin="and_(tenant_id == Action.tenant_id, id == Action.user_id)", ) class Action(Base): __tablename__ = "action" id: Mapped[UUID] = mapped_column(Uuid(), primary_key=True) tenant_id: Mapped[UUID] = mapped_column(Uuid()) user_id: Mapped[UUID] = mapped_column(Uuid(), ForeignKey("user.id")) user: Mapped["User"] = relationship("User", back_populates="actions", foreign_keys=[user_id], primaryjoin=sa.and_(tenant_id == User.tenant_id, user_id == User.id), )   The above works, but having that primaryjoin="and_(tenant_id == Action.tenant_id, id == Action.user_id)" line where the heavy lifting is done in a context opaque to static analysis is unfortunate. If we could provide code that's evaluated after all types are defined, but before SQLAlchemy begins its introspection, that would allow a helper function to be used to generate the relationships. This still isn't static-checking friendly, but it's considerably better than nothing. However, I don't know if when the relevant introspection happens (if relationships need to exist when __init_subclass__ is called, anything trying to add them later would be too late). I'd also be happy with any kind of situation where I'm using strings that static analysis can validate to be legitimate forward references -- if instantiating typing.ForwardRef("Action.tenant_id") were treated by pyright as an indication that a warning should be thrown if Action.tenant_id doesn't eventually exist, that would be perfect. SQLAlchemy has quite a bit by way of facilities I'm immediately unfamiliar with, so I'm hoping there's an option I'm not thinking of here.

Answer: Pretty sure you can use lambdas or functions like this: (untested)  class User(Base): __tablename__ = "user" id: Mapped[UUID] = mapped_column(Uuid(), primary_key=True) tenant_id: Mapped[UUID] = mapped_column(Uuid()) actions: Mapped["Action"] = relationship(lambda: Action, back_populates="user", foreign_keys=lambda: Action.user_id, primaryjoin=lambda: and_(tenant_id == Action.tenant_id, id == Action.user_id), )