You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I used the GitHub search to find a similar question and didn't find it.
I searched the SQLModel documentation, with the integrated search.
I already searched in Google "How to X in SQLModel" and didn't find any information.
I already read and followed all the tutorial in the docs and didn't find an answer.
I already checked if it is not related to SQLModel but to Pydantic.
I already checked if it is not related to SQLModel but to SQLAlchemy.
Commit to Help
I commit to help with one of those options 👆
Example Code
fromtypingimportAnnotatedfromuuidimportUUIDfrompydanticimportFieldasPydanticFieldfromsqlmodelimportSQLModel, FieldasSQLField# ---- Reusable alias with Annotated ----
type CreatedByUserId=Annotated[
UUID,
PydanticField(description="User who created this resource"),
]
# ---- ORM model using the alias directly ----classMyModel(SQLModel, table=True):
id: UUID=SQLField(primary_key=True)
created_by_user_id: CreatedByUserId=SQLField(foreign_key="auth_user.id", index=True)
Description
🧭 Context / Goal
I define type aliases using typing.Annotated + pydantic.Field(...) to keep all validation rules and descriptions in one place.
I want to:
Reuse those aliases on DTOs (Create/Update/Info) and my ORM models (table=True),
So I can access description, max_length, etc. on the ORM fields (e.g., for API, UI, ...), without duplicating Field definitions across DTOs and ORM models.
This worked until I hit a runtime error once I started using those aliases directly on ORM fields.
Traceback (shortened):
File ".../sqlmodel/main.py", line 659, in get_sqlalchemy_type
if issubclass(type_, Enum):
TypeError: issubclass() arg 1 must be a class
If I replace CreatedByUserId with plain UUID in the ORM model, it works (but I lose my DRY metadata).
🔍 What’s really happening
field.annotation that SQLModel inspects is not a plain class but an AnnotatedAlias (Pydantic v2).
SQLModel’s get_sqlalchemy_type does issubclass(type_, Enum) without unwrapping the alias → TypeError.
✅ What I already tried
Removing unions like UUID | None from the alias → still crashes (so not just Union).
Keeping aliases only in DTOs and using plain types on ORM models → works but defeats my DRY goal (I’d need to repeat Field descriptions or lose them downstream).
❓ What I’m asking
Is there a supported way in SQLModel to use Annotated[...] aliases directly on ORM fields (table=True) (and keep metadata)?
e.g., a helper in SQLModel or recommended pattern to unwrap Pydantic’s Annotated before issubclass()?
If not, is the recommendation simply: don’t use Annotated[...] types on ORM models and keep them only in DTOs?
If yes, how do people keep descriptions/constraints DRY between DTOs and ORM? Any pattern to “import” Pydantic Field metadata into SQLField(...)?
💡 Desired outcome
Single source of truth for field metadata (descriptions, max_length, etc.)
No runtime crashes 🙂
🗂️ Bigger picture (why I care)
I want to keep:
type Name=Annotated[str, Field(max_length=255, description="Reference name shown in UI")]
…and use it everywhere:
classThing(ORMModel, table=True):
name: Name=SQLField(index=True)
classThingCreate(CreateDTO):
name: Name
This gives my service layer (and even UI scaffolding) access to the same description without copying it around.
When I switch to plain str in the ORM model, I lose that Field metadata on the ORM instance and have to duplicate it on the DTOs or somewhere else.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
First Check
Commit to Help
Example Code
Description
🧭 Context / Goal
I define type aliases using typing.Annotated + pydantic.Field(...) to keep all validation rules and descriptions in one place.
I want to:
This worked until I hit a runtime error once I started using those aliases directly on ORM fields.
Traceback (shortened):
If I replace CreatedByUserId with plain UUID in the ORM model, it works (but I lose my DRY metadata).
🔍 What’s really happening
✅ What I already tried
❓ What I’m asking
💡 Desired outcome
🗂️ Bigger picture (why I care)
I want to keep:
…and use it everywhere:
This gives my service layer (and even UI scaffolding) access to the same description without copying it around.
When I switch to plain str in the ORM model, I lose that Field metadata on the ORM instance and have to duplicate it on the DTOs or somewhere else.
Operating System
Linux
Operating System Details
No response
SQLModel Version
0.0.24
Python Version
3.12
Additional Context
No response
Beta Was this translation helpful? Give feedback.
All reactions