Skip to content

[BUG] Variables with annotation of 'typing.Literal' causes a panic #57

Closed

Description

@faresbakhit

First Check

  • I added a very descriptive title to this issue.
  • 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

from sqlmodel import SQLModel
from typing import Literal


class Cool(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.

Activity

  1. shifqu commented on Nov 29, 2021

    @shifqu

    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_
    if is_literal_type(field_type):
        # Try to get the single type of the literal, error otherwise
        types = list({type(x) for x in get_args(field_type)})
        if len(types) != 1:
            raise RuntimeError("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 :)

    In your case:

    from enum import Enum
    
    class NameEnum(str, Enum):
        FASTAPI = "fastapi"
        SQLMODEL = "sqlmodel"
    
    
    class Cool(SQLModel, table=True):
        name: NameEnum
  2. data-djinn commented on Jun 15, 2022

    @data-djinn

    @shifqu this solved my problem, thanks!

  3. Cielquan commented on Aug 24, 2022

    @Cielquan

    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.

  4. Cielquan commented on Aug 24, 2022

    @Cielquan

    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.

  5. sasilver75 commented on Feb 17, 2023

    @sasilver75

    I have this issue when trying to use a Discriminated Union on a SQLModel a la Pydantic

  6. sietse commented on May 23, 2023

    @sietse

    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.

  7. PaleNeutron commented on Mar 29, 2024

    @PaleNeutron

    In your case:

    from enum import Enum
    
    class NameEnum(str, Enum):
        FASTAPI = "fastapi"
        SQLMODEL = "sqlmodel"
    
    
    class Cool(SQLModel, table=True):
        name: NameEnum

    I have to emphasis this code actually write FASTAPI uppercase, the enum key into database, not "fastapi" lower case, the value.

  8. zsiegel92 commented on Aug 1, 2024

    @zsiegel92

    In your case:

    from enum import Enum
    
    class NameEnum(str, Enum):
        FASTAPI = "fastapi"
        SQLMODEL = "sqlmodel"
    
    
    class Cool(SQLModel, table=True):
        name: NameEnum

    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).

  9. jpizquierdo commented on Oct 16, 2024

    @jpizquierdo
    Contributor

    Hello,
    In my case to make it work properly:

    from sqlmodel import SQLModel, Field, String
    from typing import Literal
    class Cool(SQLModel, table=True):
        name: Literal["fastapi", "sqlmodel"] = Field(sa_type=String)
  10. linked a pull request that will close this issue✨ Add support for `Literal` types #1439on Aug 5, 2025
  11. YuriiMotov commented on Oct 1, 2025

    @YuriiMotov
    Member

    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.

  12. added
    featureNew feature or request
    and removed
    questionFurther information is requested
    on May 18, 2026
  13. locked and limited conversation to collaborators on May 18, 2026
  14. converted this issue into a discussion #1921 on May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions