Skip to content

🐛 Fix instance of related object added to session on validation#1052

Closed
mskrip wants to merge 8 commits into
fastapi:mainfrom
mskrip:main
Closed

🐛 Fix instance of related object added to session on validation#1052
mskrip wants to merge 8 commits into
fastapi:mainfrom
mskrip:main

Conversation

@mskrip
Copy link
Copy Markdown

@mskrip mskrip commented Aug 7, 2024

Previously, when validating instance when a session was open and the model instance had a related object a new instance of this related object was created and added to the session.

I encountered similar issue described in #897 and narrowed it down to this replicatable issue:

UPD YuriiMotov: code example provided by author was invalid. Here is a working MRE from tests:

from typing import List, Optional
from sqlmodel import Field, Relationship, Session, SQLModel, create_engine


class Team(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    heroes: List["Hero"] = Relationship(back_populates="team")

class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str

    team_id: Optional[int] = Field(default=None, foreign_key="team.id")
    team: Optional[Team] = Relationship(back_populates="heroes")

engine = create_engine("sqlite://")
SQLModel.metadata.create_all(engine)
team = Team(name="team")
hero = Hero(name="hero", team=team)
with Session(engine) as session:
    session.add(team)
    session.add(hero)
    session.commit()

with Session(engine) as session:
    hero = session.get(Hero, 1)
    assert session._is_clean()

    new_hero = Hero.model_validate(hero)

    assert session._is_clean()  # AssertionError

    assert id(new_hero) != id(hero)
    assert id(new_hero.team) == id(hero.team)
original code example provided by author
from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select


class Team(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str

    heroes: list["Hero"] = Relationship(
        back_populates="team",
    )


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)

    name: str

    team_id: int | None = Field(default=None, foreign_key="team.id")
    team: Team | None = Relationship(back_populates="heroes")


if __name__ == "__main__":
    engine = create_engine(
        "postgresql+psycopg://<user>:<password>@localhost:5432/sqlmodel-test",
    )
    # SQLModel.metadata.create_all(engine)

    with Session(engine) as session:
        hero = session.exec(select(Hero)).one()
        print(f"{session.dirty=}")  # prints `session.dirty=IdentitySet([])`
        Hero.model_validate(hero, session=session)
        print(f"{session.dirty=}")  # prints `session.dirty=IdentitySet([Team(id=1, name='Team 1')])`

@mskrip mskrip changed the title Fix instance of related object added to session on validation 🐛 Fix instance of related object added to session on validation Aug 7, 2024
mskrip added 3 commits August 9, 2024 10:48
Previously, when validating instance when a session was open and the
model instance had a related object a new instance of this related
object was created and added to the session.
@alejsdev alejsdev added the bug Something isn't working label Sep 1, 2024
@svlandeg svlandeg self-assigned this Sep 17, 2025
@svlandeg svlandeg removed their assignment Oct 10, 2025
@mskrip mskrip closed this by deleting the head repository Dec 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants