Search code examples
pythonsqlalchemy

SQLAlchemy relationship with models in separate files


Can someone tell me how to use SQLAlchemy relationship if each model is in a separate file? Example:

# ./models/user.py
class UserModel(Base):
    __tablename__ = 'users'
    id: Mapped[int] = mapped_column(primary_key=True)
    characters: Mapped[list['CharacterModel']] = relationship(back_populates='user')

# ./models/character.py
class CharacterModel(Base):
    __tablename__ = 'characters'
    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int] = mapped_column(ForeignKey('users.id', ondelete='CASCADE'))
    characters: Mapped['UserModel'] = relationship(back_populates='characters')

# ./models/__init__.py
from . import user, character

Error when creating a user:

sqlalchemy.exc.InvalidRequestError: When initializing mapper Mapper[UserModel(users)], expression 'CharacterModel' failed to locate a name ('CharacterModel'). If this is a class name, consider adding this relationship() to the <class 'python.database.models.user.UserModel'> class after both dependent classes have been defined.

I can't find information anywhere, and in the use cases the models are always in the same file. I will be grateful for any information.


Solution

  • I found a solution, probably the only correct one. Everything is the same as in the code above, but at the end imports are added with the comment # noqa: E402 and add import from __future__ import annotations.

    # ./models/user.py
    from __future__ import annotations
    
    class UserModel(Base):
        __tablename__ = 'users'
        id: Mapped[int] = mapped_column(primary_key=True)
        characters: Mapped[list['CharacterModel']] = relationship(back_populates='user')
    
    from .character import CharacterModel # noqa: E402
    
    
    # ./models/character.py
    from __future__ import annotations
    
    class CharacterModel(Base):
        __tablename__ = 'characters'
        id: Mapped[int] = mapped_column(primary_key=True)
        user_id: Mapped[int] = mapped_column(ForeignKey('users.id', ondelete='CASCADE'))
        characters: Mapped['UserModel'] = relationship(back_populates='characters')
    
    from .user import UserModel # noqa: E402
    
    
    # ./models/__init__.py
    from . import user, character