Repository navigation
from __future__ import annotation and many to many example #196
Description
Activity
Hi,
I have the same issue with sqlmodel 0.0.6 and python 3.9.9.I have created a similar virtual env with python 3.8.10 and this piece of code works as expected.
I had a similar issue with 1-M relationship, where the models are also separated into their own files.
sqlmodel == 0.0.6 python == 3.9Not sure if that helps, but I was able to workaround this issue by removing
from __future__ import annotations
from the file of the "parent" model and returning back to quoting the type hints:
# other imports omitted from typing import TYPE_CHECKING: from .child import ChildModel class ParentModel(SQLModel, table=True): # children: List[ChildModel] = Relationship(back_populates="parent") # does not work children: List["ChildModel"] = Relationship(back_populates="parent") # OK # other column definitions...
Reacted by lovetoburnswhen, Piotr Kardaś, Brian Metzler, Ilja Gubins, Renato Caruso, couteau, loïc poisot and hirotoReacted by Florentin GERARD, Ilja Gubins and Renato CarusoI had a similar issue with 1-M relationship, where the models are also separated into their own files.
sqlmodel == 0.0.6 python == 3.9@l1b3r
Do you updated to lastest versions?I am having some problems with the models separated in different files.
The example model of Heroes and Teams, works fine with separate files until I add the model that allows to get a Team with its Heroes.
https://sqlmodel.tiangolo.com/tutorial/fastapi/relationships/#update-the-path-operationsAre you using this option, does it work for you?
Thanks
It looks like the forward reference is not evaluated at all, but passed to SQLAlchemy as-is.
I experienced the same issue on version
sqlmodel == 0.0.8This is a pretty confusing problem to have given the selling point of this library.
Postponed annotations are still an issue with
0.0.14, hence the ruff setting.
I guess it has to do with all thevery dark black magicthatSQLAlchemydoes 😅I also hit this issue (again) with a new project, But removing
from __future__ import annotations
fixed it (again).
Just in case someone else hits this issueReacted by Anthony Holten, rysson, Abrahan Munro and ChanminloThank you @s-weigand, I removed the
annotationsimport and SQLAlchemy stopped complaining that classes weren't mapped!Would have saved me time if I was told that future annotations cannot be used, and that forward type references in SQLModel classes should be enclosed in double quotes to play nice with SA. I guess I should have gleaned this from the docs, on the page about type annotation strings? Considering this issue thread though, I think it's worth addressing explicitly somewhere. I'll look into adding this caveat to the docs if contributions are accepted.
yeah i lost loads of time to this, definitely think 'don't import annotations from future' should be stated somewhere in the docs. unless i missed it?
Reacted by Anthony Holten and Daniel CzarnieviczWhile documentation is important, is there a plan or a route to fix this? Strings don't work for nice things like
Optional[A | B | C]...While documentation is important, is there a plan or a route to fix this? Strings don't work for nice things like
Optional[A | B | C]...I think you can do something like this
Optional["A" | "B" | "C"]@DirkReiners Forward referenced type annotations are a planned future feature for Python, so I wouldn't expect this library to implement a fix.
I explain the situation and state of the feature in a bit more detail in my PR
Just to double check - this isn't something that would be fixed by adding
typing.get_type_hints(...)somewhere, would it?Reacted by ryssonSeems reasonable to me that it would fix the problem.
Having the same issue.
I had a similar issue with 1-M relationship, where the models are also separated into their own files.
sqlmodel == 0.0.6 python == 3.9Not sure if that helps, but I was able to workaround this issue by removing
from __future__ import annotations
from the file of the "parent" model and returning back to quoting the type hints:
# other imports omitted from typing import TYPE_CHECKING: from .child import ChildModel class ParentModel(SQLModel, table=True): # children: List[ChildModel] = Relationship(back_populates="parent") # does not work children: List["ChildModel"] = Relationship(back_populates="parent") # OK # other column definitions...
Although this workaround works for the time being, the code will no longer work in the future (when __future__.annotations become the default), so this should be fixed.
It is possible to do the Many to Many example from the initial post with future annotations by overwriting the relationships with sqlalchemy's sa_relatiopnships.
The following code works and the only changes from the original post are changing
Optionalto|,Listtolist, the definition ofheroswithinTeamandteamswithinHeros. Note:secondaryneeds to be set to the tablename. Ideally we would pass the table object, but we don't have access to that, but sqlalchemy allows passing of the tablename as a string instead.Obviously it is a bit of a pain to change the SQLModel Relationship to a raw sqlalchemy relationship, but at least then it works...
I guess in theory it should be possible to update the standard SQLModel Relationship to follow this pattern and have it work fine with__future__.annotationswhich is obviously desirable (and will probably be come mandatory in like.... 3.14 or something, maybe... if the Python devs ever actually get around to pulling the trigger on making postponed evaluation of annotations mandatory...Note: Obviously this only fixes the original M-M issue. I still have issues with M-M or 1-M or M-1 when models are in different files... for now if I import
annotationsthen all my tables have to sit in one file. Which is not great...from __future__ import annotations from sqlalchemy.orm import Mapped, relationship from sqlmodel import Field, Relationship, Session, SQLModel, create_engine class HeroTeamLink(SQLModel, table=True): team_id: int | None = Field( default=None, foreign_key='team.id', primary_key=True, ) hero_id: int | None = Field( default=None, foreign_key='hero.id', primary_key=True, ) class Team(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) name: str headquarters: str heroes: Mapped[list[Hero]] = Relationship( sa_relationship=relationship(back_populates='teams', secondary='heroteamlink'), ) class Hero(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) name: str secret_name: str age: int | None = None teams: Mapped[list[Team]] = Relationship( sa_relationship=relationship(back_populates='heroes', secondary='heroteamlink'), ) sqlite_url = 'sqlite://' engine = create_engine(sqlite_url, echo=True) def create_db_and_tables(): SQLModel.metadata.create_all(engine) def create_heroes(): with Session(engine) as session: team_preventers = Team(name='Preventers', headquarters='Sharp Tower') team_z_force = Team(name='Z-Force', headquarters='Sister Margaret’s Bar') hero_deadpond = Hero( name='Deadpond', secret_name='Dive Wilson', teams=[team_z_force, team_preventers], ) hero_rusty_man = Hero( name='Rusty-Man', secret_name='Tommy Sharp', age=48, teams=[team_preventers], ) hero_spider_boy = Hero( name='Spider-Boy', secret_name='Pedro Parqueador', teams=[team_preventers], ) session.add(hero_deadpond) session.add(hero_rusty_man) session.add(hero_spider_boy) session.commit() session.refresh(hero_deadpond) session.refresh(hero_rusty_man) session.refresh(hero_spider_boy) print('Deadpond:', hero_deadpond) print('Deadpond teams:', hero_deadpond.teams) print('Rusty-Man:', hero_rusty_man) print('Rusty-Man Teams:', hero_rusty_man.teams) print('Spider-Boy:', hero_spider_boy) print('Spider-Boy Teams:', hero_spider_boy.teams) def main(): create_db_and_tables() create_heroes() if __name__ == '__main__': main()As the comment directly above mentioned, this is becoming more of an issue in Python 3.14 (which has been released by now), where unquoted forward annotations don't even need a
__future__import anymore, so I expect newcomers from now on won't generally even know that there is such a thing as quoted forward references.- locked and limited conversation to collaborators
on May 18, 2026
First Check
Commit to Help
Example Code
Description
all what i have done is use the example from the docs about many to many relationships and tried to fit them with
from __future__ import annotationby adding the import in the first line and changingList["Hero"]toList[Hero]. and i get this erroralso
update_forward_refs()did not help for both classes.Operating System
Linux
Operating System Details
No response
SQLModel Version
0.0.4
Python Version
Python 3.9.9
Additional Context
No response