Replies: 3 comments 4 replies
-
I’ve encountered the same issue myself. |
Beta Was this translation helpful? Give feedback.
-
I've also been facing this issue. I found a workaround here, but it ain't pretty. You can move the circular imports to the bottom of the file and call ############ models/hero.py ############
from typing import Optional, TYPE_CHECKING
"""
More code here...
"""
class HeroReadWithTeam(HeroRead):
team: Optional["TeamRead"] = None
# At the bottom of the file
from .team import TeamRead
HeroReadWithTeam.model_rebuild()
############ models/team.py ############
from typing import Optional, List, TYPE_CHECKING
"""
More code here...
"""
class TeamReadWithHeroes(TeamRead):
heroes: List["HeroRead"] = []
# At the bottom of the file
from .hero import TeamReadWithHeroes
TeamReadWithHeroes.model_rebuild() Explanation: the first time Python tries to import from a file, it builds a In our case, Python is already aware of the models it needs from |
Beta Was this translation helpful? Give feedback.
-
Using:
And the examples provided in SQLModel Tutorial: Is this THE solution to go with or what would you recommend @tiangolo ? See this code, combining above mentioned 3 sources. This is a 100% working full example:
hero_model.py: from typing import TYPE_CHECKING, Optional
from sqlmodel import Field, Relationship, SQLModel
if TYPE_CHECKING:
from team_model import Team, TeamPublic
class HeroBase(SQLModel):
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
team_id: int | None = Field(default=None, foreign_key="team.id")
class Hero(HeroBase, table=True):
id: int | None = Field(default=None, primary_key=True)
team: Optional["Team"] = Relationship(back_populates="heroes")
class HeroPublic(HeroBase):
id: int
class HeroCreate(HeroBase):
pass
class HeroUpdate(SQLModel):
name: str | None = None
secret_name: str | None = None
age: int | None = None
team_id: int | None = None
class HeroPublicWithTeam(HeroPublic):
team: Optional["TeamPublic"] = None
# fix 'PydanticUndefinedAnnotation: name 'TeamPublic' is not defined' error
# see: https://github.com/tiangolo/sqlmodel/discussions/757
from team_model import TeamPublic
HeroPublicWithTeam.model_rebuild() team_model.py: from typing import TYPE_CHECKING
from sqlmodel import Field, Relationship, SQLModel
if TYPE_CHECKING:
from hero_model import Hero, HeroPublic
class TeamBase(SQLModel):
name: str = Field(index=True)
headquarters: str
class Team(TeamBase, table=True):
id: int | None = Field(default=None, primary_key=True)
heroes: list["Hero"] = Relationship(back_populates="team")
class TeamCreate(TeamBase):
pass
class TeamPublic(TeamBase):
id: int
class TeamUpdate(SQLModel):
id: int | None = None
name: str | None = None
headquarters: str | None = None
class TeamPublicWithHeroes(TeamPublic):
heroes: list["HeroPublic"] = []
# fix 'PydanticUndefinedAnnotation: name 'HeroPublic' is not defined' error
# see: https://github.com/tiangolo/sqlmodel/discussions/757
from hero_model import HeroPublic
TeamPublicWithHeroes.model_rebuild() main.py: #!/usr/bin/env python
from fastapi import Depends, FastAPI, HTTPException, Query
from sqlmodel import Session, SQLModel, create_engine, select
import uvicorn
from hero_model import Hero, HeroCreate, HeroPublic, HeroPublicWithTeam, HeroUpdate
from team_model import Team, TeamCreate, TeamPublic, TeamPublicWithHeroes, TeamUpdate
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, echo=True, connect_args=connect_args)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def get_session():
with Session(engine) as session:
yield session
app = FastAPI()
@app.on_event("startup")
def on_startup():
create_db_and_tables()
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
session.commit()
session.refresh(db_hero)
return db_hero
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
offset: int = 0,
limit: int = Query(default=100, le=100),
):
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes
@app.get("/heroes/{hero_id}", response_model=HeroPublicWithTeam)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate):
db_hero = session.get(Hero, hero_id)
if not db_hero:
raise HTTPException(status_code=404, detail="Hero not found")
hero_data = hero.model_dump(exclude_unset=True)
for key, value in hero_data.items():
setattr(db_hero, key, value)
session.add(db_hero)
session.commit()
session.refresh(db_hero)
return db_hero
@app.delete("/heroes/{hero_id}")
def delete_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
session.delete(hero)
session.commit()
return {"ok": True}
@app.post("/teams/", response_model=TeamPublic)
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
db_team = Team.model_validate(team)
session.add(db_team)
session.commit()
session.refresh(db_team)
return db_team
@app.get("/teams/", response_model=list[TeamPublic])
def read_teams(
*,
session: Session = Depends(get_session),
offset: int = 0,
limit: int = Query(default=100, le=100),
):
teams = session.exec(select(Team).offset(offset).limit(limit)).all()
return teams
@app.get("/teams/{team_id}", response_model=TeamPublicWithHeroes)
def read_team(*, team_id: int, session: Session = Depends(get_session)):
team = session.get(Team, team_id)
if not team:
raise HTTPException(status_code=404, detail="Team not found")
return team
@app.patch("/teams/{team_id}", response_model=TeamPublic)
def update_team(
*,
session: Session = Depends(get_session),
team_id: int,
team: TeamUpdate,
):
db_team = session.get(Team, team_id)
if not db_team:
raise HTTPException(status_code=404, detail="Team not found")
team_data = team.model_dump(exclude_unset=True)
for key, value in team_data.items():
setattr(db_team, key, value)
session.add(db_team)
session.commit()
session.refresh(db_team)
return db_team
@app.delete("/teams/{team_id}")
def delete_team(*, session: Session = Depends(get_session), team_id: int):
team = session.get(Team, team_id)
if not team:
raise HTTPException(status_code=404, detail="Team not found")
session.delete(team)
session.commit()
return {"ok": True}
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8888) |
Beta Was this translation helpful? Give feedback.
-
First Check
Commit to Help
Example Code
Description
As in the example provided on the website, I want to use Models with Relationships in FastAPI. It works fine when I work within a single file. However, I'd like to split the models into different files. When I do so (as in the code example I provided), I encounter the following error upon execution:
The problem is in this code :
Has anyone else encountered this issue and can help me?
Operating System
macOS
Operating System Details
Sonoma 14.1.1
SQLModel Version
0.0.14
Python Version
3.12.0
Additional Context
FastAPI version : 0.105.0
Pydantic version : 2.5.2
Beta Was this translation helpful? Give feedback.
All reactions