Describe the bug
Passing table=True together with a custom registry= stores the boolean table flag into model_config["registry"] instead of the registry object, and the model is left unmapped.
In sqlmodel/main.py (SQLModelMetaclass):
config_registry = get_config("registry")
if config_registry is not Undefined:
config_registry = cast(registry, config_registry)
# If it was passed by kwargs, ensure it's also set in config
new_cls.model_config["registry"] = config_table # BUG: should be config_registry
setattr(new_cls, "_sa_registry", config_registry)
setattr(new_cls, "metadata", config_registry.metadata)
setattr(new_cls, "__abstract__", True)
Debug information
- Repo checkout @
803a3d1f6d605a1da45ed166120845ed54ce9c2a
- Python 3.12
Repro steps
from sqlalchemy.orm import registry
from sqlmodel import SQLModel, Field
reg = registry()
class Hero(SQLModel, table=True, registry=reg):
id: int | None = Field(default=None, primary_key=True)
name: str
print(Hero.model_config["registry"]) # True (expected: the registry object)
print(hasattr(Hero, "__mapper__")) # False
# Session usage typically raises sqlalchemy.orm.exc.UnmappedClassError
# Subclassing also breaks: AttributeError: 'bool' object has no attribute 'metadata'
Default path works: class Hero(SQLModel, table=True).
Expected behavior
model_config["registry"] should hold the provided registry; the table model should be mapped like the default-registry table=True path.
Actual behavior
model_config["registry"] is True; model has no mapper; multi-registry one-shot API is unusable.
Suggested direction
new_cls.model_config["registry"] = config_registry
Also reconsider always setting __abstract__ = True when a custom registry is provided on a table=True model.
I'd be happy to prepare a PR with a regression test if this is confirmed.
Describe the bug
Passing
table=Truetogether with a customregistry=stores the booleantableflag intomodel_config["registry"]instead of the registry object, and the model is left unmapped.In
sqlmodel/main.py(SQLModelMetaclass):Debug information
803a3d1f6d605a1da45ed166120845ed54ce9c2aRepro steps
Default path works:
class Hero(SQLModel, table=True).Expected behavior
model_config["registry"]should hold the provided registry; the table model should be mapped like the default-registrytable=Truepath.Actual behavior
model_config["registry"]isTrue; model has no mapper; multi-registry one-shot API is unusable.Suggested direction
Also reconsider always setting
__abstract__ = Truewhen a custom registry is provided on atable=Truemodel.I'd be happy to prepare a PR with a regression test if this is confirmed.