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
{{ message }}
Repository navigation
[BUG] Variables with annotation of 'typing.Literal' causes a panic #57
I used the GitHub search to find a similar issue 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
fromsqlmodelimportSQLModelfromtypingimportLiteralclassCool(SQLModel, table=True):
name: Literal["fastapi", "sqlmodel"]
# This code outputs the following to stderr# File "main.py", line 5, in <module># class Cool(SQLModel, table=True):# File "sqlmodel/main.py", line 292, in __new__# col = get_column_from_field(v)# File "sqlmodel/main.py", line 415, in get_column_from_field# sa_type = get_sqlachemy_type(field)# File "sqlmodel/main.py", line 373, in get_sqlachemy_type# if issubclass(field.type_, str):# TypeError: issubclass() arg 1 must be a class
Description
Create a sub-class of sqlmodel.main.SQLModel with table=True
Add a variable and annotate it with typing.Literal[...]
Python raises a TypeError exception at sqlmodel/main:get_sqlachemy_type
This happens because typing.Literal[...] is a function and since SQLModel uses issubclass to get the variable's SQLAlchemy type; issubclass just throws a TypeError since it expects a class.
Operating System
Linux
Operating System Details
No response
SQLModel Version
0.0.4
Python Version
Python 3.9.6
Additional Context
Funny enough Pydantic had quite the same issue pydantic/pydantic#1026 so it can be as easy as importing pydantic.typing.is_literal_type and running an if statement at the top of get_sqlachemy_type
I think the real issue is that typing.Literal is an 'Union' type it receive one or more values but since it's often used for one type (e.g. Literal["r", "w", ...]) we can return the type of all the passed values if they are all the same else None. The values can be retrieved with typing.get_args as a tuple.
I also encountered this issue and ended up implementing the check for a Literal at the beginning of get_sqlachemy_type.
field_type=field.type_ifis_literal_type(field_type):
# Try to get the single type of the literal, error otherwisetypes=list({type(x) forxinget_args(field_type)})
iflen(types) !=1:
raiseRuntimeError("Cannot map a literal with multiple types to a sql type.")
field_type=types[0]
To make this work you would still need to import get_args and is_literal_type, also refactor each field.type_ call to field_type
The problem with this solution is that it is specifically for a Literal. So, not sure if this will be immediately accepted to be implemented. Happy to make this into a PR if needed.
I eventually stopped using Literal, and chose for an Enum, which works out of the box :)
I have the same issue and went with the enum solution. But the Literal approach would be still worth adding I guess, for the future to open up the possibilities.
I have to emphasis this code actually write FASTAPI uppercase, the enum key into database, not "fastapi" lower case, the value.
Yes, inserting values like NameEnum.FASTAPI above will insert 'FASTAPI' in your database row.
In my opinion this is not the behavior anyone should expect since in general database enums can have spaces in them and therefore users should expect SQLModel to use the .value property of enum values when inserting an enum (since the alternative makes it impossible to insert enum values with spaces via SQLModel).
Reacted by Atomie CHEN, Shuto Otaki, shineslove, Martin Šmídl, Ventura Rivera, Jeremy A. Prescott, Patrick, Jakub Kadlčík, Ben Mares, Aimore and 9 more
Reacted by Marco Lehner, Niklas, Chris Rahmé and shuc
I attempted to implement this and I have a question: would you expect any kind of validation for columns declared as Literal?
For example, the field declared as name: Literal["fastapi", "sqlmodel"], but in the database there is a value typer. If we query this object, we will have invalid value for name field and it can cause errors in user code.
Activity
shifqu commentedon Nov 29, 2021
I also encountered this issue and ended up implementing the check for a Literal at the beginning of
get_sqlachemy_type.To make this work you would still need to import
get_argsandis_literal_type, also refactor eachfield.type_call tofield_typeThe problem with this solution is that it is specifically for a Literal. So, not sure if this will be immediately accepted to be implemented. Happy to make this into a PR if needed.
I eventually stopped using Literal, and chose for an Enum, which works out of the box :)
In your case:
data-djinn commentedon Jun 15, 2022
@shifqu this solved my problem, thanks!
Cielquan commentedon Aug 24, 2022
I have the same issue and went with the enum solution. But the
Literalapproach would be still worth adding I guess, for the future to open up the possibilities.Cielquan commentedon Aug 24, 2022
I encountered an issue with the enum approach (see #406) where the validation for the field with the enum values silently passes for invalid values.
sasilver75 commentedon Feb 17, 2023
I have this issue when trying to use a Discriminated Union on a SQLModel a la Pydantic
sietse commentedon May 23, 2023
EDIT: See pydantic/pydantic#5821 (comment) for the main issue and a list of workarounds.
I'm deleting the rest of my comment: all its information is present in the linked thread.
PaleNeutron commentedon Mar 29, 2024
I have to emphasis this code actually write
FASTAPIuppercase, the enum key into database, not"fastapi"lower case, the value.Fix bug with sqlmodel: TypeError: issubclass() arg 1 must be a class
zsiegel92 commentedon Aug 1, 2024
Yes, inserting values like
NameEnum.FASTAPIabove will insert'FASTAPI'in your database row.In my opinion this is not the behavior anyone should expect since in general database enums can have spaces in them and therefore users should expect SQLModel to use the
.valueproperty of enum values when inserting an enum (since the alternative makes it impossible to insert enum values with spaces via SQLModel).jpizquierdo commentedon Oct 16, 2024
Hello,
In my case to make it work properly:
YuriiMotov commentedon Oct 1, 2025
I attempted to implement this and I have a question: would you expect any kind of validation for columns declared as
Literal?For example, the field declared as
name: Literal["fastapi", "sqlmodel"], but in the database there is a valuetyper. If we query this object, we will have invalid value fornamefield and it can cause errors in user code.