Skip to content

sqlmodel_update() overwrites all fields from BaseModel input, silently destroying data in PATCH endpoints #1835

@mahdirajaee

Description

@mahdirajaee

Description

When sqlmodel_update() receives a BaseModel instance, it iterates over every field and calls setattr for each one — including fields the caller never set. There is no exclude_unset parameter and no awareness of __pydantic_fields_set__.

Reproduction

class HeroUpdate(SQLModel):
    name: str | None = None
    age: int | None = None

@app.patch("/heroes/{hero_id}")
def update_hero(hero_id: int, hero_update: HeroUpdate, session: Session = Depends(get_session)):
    db_hero = session.get(Hero, hero_id)
    db_hero.sqlmodel_update(hero_update)  # Overwrites age=None even if client only sent name
    session.add(db_hero)
    session.commit()

Client sends {"name": "New Name"}. hero_update.age defaults to None. The method overwrites the hero's existing age with None, silently destroying data.

Current Workaround

db_hero.sqlmodel_update(hero_update.model_dump(exclude_unset=True))

This works but is non-obvious. The method's type signature explicitly accepts BaseModel, implying it handles this case correctly.

Proposed Fix

Add an exclude_unset: bool = False parameter to sqlmodel_update(). When True and obj is a BaseModel, filter to only obj.__pydantic_fields_set__ before applying:

def sqlmodel_update(self, obj, *, exclude_unset: bool = False):
    if isinstance(obj, BaseModel):
        if exclude_unset:
            data = obj.model_dump(exclude_unset=True)
        else:
            data = {field: getattr(obj, field) for field in get_model_fields(obj)}
    # ...

This is the single most common FastAPI + SQLModel pattern and the silent data corruption is a significant footgun.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions