Skip to content

Instantly share code, notes, and snippets.

@DanielGibson
Created February 27, 2026 18:49
Show Gist options
  • Select an option

  • Save DanielGibson/05a8b3dc604cddeb09f30cf0aa0b17b2 to your computer and use it in GitHub Desktop.

Select an option

Save DanielGibson/05a8b3dc604cddeb09f30cf0aa0b17b2 to your computer and use it in GitHub Desktop.
Reproducer for SQLModel -> JSON -> SQLModel bug
# Minimal reproducer for bugs and inconsistencies around SQLModel->Json->SQLModel roundtrips
# Basically: For SQLModels with table=True, model_validate_json(jstr) is broken,
# and so is model_validate( json.loads(jstr) ), but in slightly different ways.
#
# model_validate_json(jstr) does not restore non-JSON types properly when used with a table=True SQLModel
# For example, UUIDs remain strings.
#
# model_validate( json.loads(jstr) ) *does* restore those types properly when used directly on
# an SQLModel table=True type, but when that SQLModel is a member of another type it's broken in
# the same way as the former case (e.g. UUIDs remain strings)
#
# Weirdly, for SQLModels with table=False everything seems to work.
import json
import traceback
import warnings
from uuid import uuid4, UUID
from pydantic import BaseModel
from sqlmodel import SQLModel, Field as SQLField
class InnerModelSQLNoTable(SQLModel, table=False):
id: UUID = SQLField(default_factory=uuid4, primary_key=True)
class InnerModelSQL(InnerModelSQLNoTable, table=True):
pass
class OuterModel(BaseModel):
im: InnerModelSQL
class OuterModelNoTable(BaseModel):
im: InnerModelSQLNoTable
def repro_impl(obj, use_model_validate_json):
cls_model = type(obj)
m = "model_validate_json(j)" if use_model_validate_json else "model_validate(json.loads(j))"
print(f"\nChecking for {cls_model.__name__}, creating copy from json with {m}")
j = obj.model_dump_json()
if use_model_validate_json:
obj2 = cls_model.model_validate_json(j)
else:
obj2 = cls_model.model_validate(json.loads(j))
im = obj2 if cls_model.__name__.startswith("Inner") else obj2.im
if isinstance(im.id, UUID):
print("... ok")
else:
print(f"!!! ERROR: InnerModel*.id is not a UUID but {type(im.id)}")
try:
obj2.model_dump_json()
except:
print("obj2.model_dump_json() failed with")
traceback.print_exc()
def repro(with_table):
if with_table:
print("\n### Testing with an InnerModel derived from SQLModel with table=True")
im = InnerModelSQL()
om = OuterModel(im=im)
else:
print("\n### Testing with an InnerModel derived from SQLModel with table=False")
im = InnerModelSQLNoTable()
om = OuterModelNoTable(im=im)
repro_impl(im, True)
repro_impl(om, True)
repro_impl(im, False)
repro_impl(om, False)
if __name__ == "__main__":
#warnings.simplefilter("error") # turn UserWarnings into exceptions
repro(False)
repro(True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment